Skip to content

Commit

Permalink
AfterTouches: standardisation, AVIWriter
Browse files Browse the repository at this point in the history
- Standardised frame reading
- Implemented AVI Writer module
- Bound F10 to AVI Writer
- Added filename to framegroup
  • Loading branch information
andrewjrobinson committed Apr 24, 2014
1 parent 0868c65 commit 457eb5f
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 15 deletions.
31 changes: 31 additions & 0 deletions sportsreview/aftertouches/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ def __init__(self, argv = [], parent=None):

self._openFrameGroup = None

self._processframerandom = {}
self._processgrouprandom = {}

# load settings file
if len(argv) == 2:
self.settings = sportsreview.settings.settingsmanager.SettingsManager(argv[1])
Expand All @@ -53,6 +56,8 @@ def __init__(self, argv = [], parent=None):

# connect signals
self.mainwindow.openFile.connect(self.openFile)
self.mainwindow.processFrame.connect(self.processFrame)
self.mainwindow.processGroup.connect(self.processGroup)

# frame playback timer
self._playing = 0 #speed, 1 = normal, 0.5 = half speed (in future: -ve for reverse)
Expand Down Expand Up @@ -147,6 +152,32 @@ def play(self, speed):
pass #TODO: alter start time so it will calculate correctly
else:
self.mainwindow.setStatusMsg("No file open!", 1000)

@Slot(str, object)
def processFrame(self, modulename, config):
''''''
if self._openFrameGroup is not None:
# get module implementation
if modulename in self._processframerandom:
module = self._processframerandom[modulename]
else:
module = sportsreview.common.modulemanager.ModuleManager.getProcessFrameModule(modulename).getModule(self.settings, config)
self._processframerandom[modulename] = module

module.process(self._openFrameGroup.current())

@Slot(str, object)
def processGroup(self, modulename, config):
''''''
if self._openFrameGroup is not None:
# get module implementation
if modulename in self._processgrouprandom:
module = self._processgrouprandom[modulename]
else:
module = sportsreview.common.modulemanager.ModuleManager.getProcessGroupModule(modulename).getModule(self.settings, config)
self._processgrouprandom[modulename] = module

module.processGroup(self._openFrameGroup)

## Support ##
def _playFrame(self):
Expand Down
14 changes: 8 additions & 6 deletions sportsreview/aftertouches/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ def __init__(self, settings, application=None):
incFrame = Signal()
decFrame = Signal()
play = Signal(float)
processFrame = Signal(str, object)
processGroup = Signal(str, object)

## Slots ##
@Slot(str,object)
Expand Down Expand Up @@ -120,7 +122,7 @@ def openFrameGroup(self, fgroup):
self.ui.frameSlider.setMaximum(frameCount -1)

# render previews
f0 = fgroup[0][0]
f0 = fgroup[0][0].asQPixmap()
previewW = self.ui.previewLabel.width()
previewH = self.ui.previewLabel.height()
previewPmap = QtGui.QPixmap(previewW, previewH)
Expand All @@ -134,20 +136,20 @@ def openFrameGroup(self, fgroup):
for i in xrange(printCount):
fid = int(round(float(i * (frameCount-1)) / (printCount-1)))
x = int(round(i * (freeSpace + scaledW)))
frame = fgroup[fid][0].scaled(scaledW, previewH)
frame = fgroup[fid][0].asQPixmap().scaled(scaledW, previewH)
painter.drawPixmap(x,0,frame)


painter.end()
self.ui.previewLabel.setPixmap(previewPmap)

# render the current frame
self.ui.frameLabel.setPixmap(fgroup.current()[0])
self.ui.frameLabel.setPixmap(fgroup.current()[0].asQPixmap())

@Slot(object, int)
def selectedFrameSet(self, fgroup, index):
'''Slot to know when the frame selection changes'''
self.ui.frameLabel.setPixmap(fgroup[index][0])
self.ui.frameLabel.setPixmap(fgroup[index][0].asQPixmap())
self.ui.frameSlider.setSliderPosition(index)

@Slot()
Expand Down Expand Up @@ -238,8 +240,8 @@ def _loadBindings(self, bindings):
try:

groups = {'core': 'core',
# 'processframe': lambda m,c: self.processFrame.emit(m,c),
# 'processgroup': lambda m,c: self.processGroup.emit(m,c),
'processframe': lambda m,c: self.processFrame.emit(m,c),
'processgroup': lambda m,c: self.processGroup.emit(m,c),
}
corefuncs = {
'quit': lambda a,b: self.close(),
Expand Down
6 changes: 6 additions & 0 deletions sportsreview/common/framegroup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def __init__(self, timestamp=None):
if timestamp is None:
timestamp = time.time()
self.timestamp = timestamp
self.filename = None
self._current = 0
self._currentset = False
self.headers = {}
Expand Down Expand Up @@ -165,6 +166,8 @@ def setHeader(self, name, value):
'''Set a header value. System headers will be made into attributes'''
if name == "timestamp":
self.timestamp = float(value)
elif name == "filename":
self.filename = value
self.headers[name] = value

def __len__(self, *args, **kwargs):
Expand Down Expand Up @@ -197,6 +200,7 @@ def __init__(self, timestamp=None, loadframelistfunc=None, loadframelistopts=Non
if timestamp is None:
timestamp = time.time()
self.timestamp = timestamp
self.filename = None
self._loadframelistfunc = loadframelistfunc
self._loadframelistopts = loadframelistopts
self._current = 0
Expand Down Expand Up @@ -276,6 +280,8 @@ def setHeader(self, name, value):
'''Set a header value. System headers will be made into attributes'''
if name == "timestamp":
self.timestamp = float(value)
elif name == "filename":
self.filename = value
self.headers[name] = value

def __len__(self, *args, **kwargs):
Expand Down
1 change: 1 addition & 0 deletions sportsreview/delayvideo/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def __init__(self, argv = [], parent=None):
self._processframes.append(sportsreview.common.modulemanager.ModuleManager.getProcessFrameModule(cap[0]).getModule(self.settings, cap[1]))
self.pausedbuffer = None # place to store frames while paused

self._processframerandom = {}
self._processgrouprandom = {}

# frame capture timer
Expand Down
5 changes: 3 additions & 2 deletions sportsreview/modules/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@

#TODO: make this not required (i.e. so we can add new modules easily)
#Hack, needs a better way
import usbcamcapturecv
import aviwritercv
import framedelay
import frameextend
import framestore
import jpegstillarrayreader
import jpegstillarraywriter
import jpegstillarraywriter
import usbcamcapturecv
101 changes: 101 additions & 0 deletions sportsreview/modules/aviwritercv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# /*******************************************************************************
# * (c) Andrew Robinson (andrewjrobinson@gmail.com) 2014 *
# * *
# * This file is part of SportsReview. *
# * *
# * SportsReview is free software: you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License as published *
# * by the Free Software Foundation, either version 3 of the License, or *
# * (at your option) any later version. *
# * *
# * SportsReview is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public License *
# * along with SportsReview. If not, see <http://www.gnu.org/licenses/>. *
# * *
# *******************************************************************************/
'''
Created on 24/04/2014
@author: Andrew Robinson
'''
import numpy as np
import os, time
import cv2
from cv2 import cv

from sportsreview.support.qtlib import QtCore, QtGui, Slot

class AviWriterCV(QtCore.QObject):
'''Writes frames from specified stream of framegroup to an AVI file using OpenCV'''

__CAPTURE_FRAME__ = False
__PROCESS_FRAME__ = False
__CAPTURE_GROUP__ = False
__PROCESS_GROUP__ = True

def __init__(self, settings, config):
'''
Writes frames from specified stream of framegroup to an AVI file using OpenCV
@param settings: global settings object (from settings file)
@param config: the specific configuration for this instance (from layout)
'''
QtCore.QObject.__init__(self)

self._settings = settings
self._stream = config[0]

self._settings.settingChanged.connect(self.settingChanged)
self._recorddirectory = settings.getSetting("recorddirectory")

@classmethod
def getModule(cls, settings, config):
'''Returns an instance of this module for the provided settings'''
return cls(settings, config)

def processGroup(self, framegroup):
'''
Writes frames from specified stream of framegroup to an AVI file using OpenCV
@param framegroup: the incoming framegroup object
'''

if framegroup is not None:
ext = "avi"
codec = "DIVX"
# calculate details
fps = int(round(len(framegroup) / (framegroup[-1].timestamp - framegroup[0].timestamp)))
f0 = framegroup[0][self._stream].asQImage()
fsize = (f0.width(),f0.height())
filename = framegroup.filename
if filename is None:
filename = "%s%svideo_%s" % (self._recorddirectory, os.path.sep, time.strftime("%Y-%m-%d_%H-%M-%S"), )
filename += ".%s" % ext

# open writer
writer = cv2.VideoWriter(filename,cv.CV_FOURCC(*codec),fps,fsize)

# write frame files
for frameset in framegroup:
frame = self._QImageToCv(frameset[self._stream].asQImage())
writer.write(frame)


@Slot(str,object)
def settingChanged(self, name, value):
if name == "recorddirectory":
self._recorddirectory = value



def _QImageToCv(self,img):
'''Convert QImage to OpenCV image format'''
img = img.convertToFormat(QtGui.QImage.Format.Format_RGB32)
nparr = np.array(img.constBits()).reshape(img.height(), img.width(), 4)
nparr = np.delete(nparr, 3, 2)
return nparr

# end class JpegStillArrayWriter
28 changes: 25 additions & 3 deletions sportsreview/modules/jpegstillarrayreader.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,17 @@ def load(self, options):
@return: FrameGroup like object
'''
def lazysetloader(sself, _, framefilename):
# print "Loading: %s" % framefilename
pmap = QtGui.QPixmap(framefilename)
pmap = QPIFrame(framefilename)
return pmap
def lazygrouploader(gself, timingfilename):
f = open(timingfilename)
result = []
gself.setHeader('filename', timingfilename)
for line in f:
if line.startswith('#'):
line = line[1:]
name, value = line.split('=', 2)
gself.headers[name] = value
gself.setHeader(name, value)
else:
cols = line.strip().split('\t')

Expand All @@ -93,5 +93,27 @@ def lazygrouploader(gself, timingfilename):


#end class JpegStillArrayReader

class QPIFrame(object):
'''Stores a frame in QPixmap or QImage format'''

def __init__(self, filename):
self._filename = filename
self._qimage = None
self._qpixmap = None

def asQPixmap(self):
'''Returns a qpixmap for this frame'''
if self._qpixmap is None:
self._qpixmap = QtGui.QPixmap(self._filename)
return self._qpixmap

def asQImage(self):
'''Returns a qimage version of this frame'''
if self._qimage is None:
self._qimage = QtGui.QImage(self._filename)
return self._qimage

# end class QPIFrame


11 changes: 7 additions & 4 deletions sportsreview/settings/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
'F1': [
('core', 'help'),
],
'F10': [
('processgroup', 'AviWriterCV', 0),
],
'F11': [
('core', 'fullscreen'),
],
Expand Down Expand Up @@ -134,14 +137,14 @@
},
],
'aftertouchesui': {
'geometry': (0, 39, 1280, 985),
'mode': 'maximised',
'geometry': (1, 39, 746, 715),
'mode': 'normal',
'savegeometry': True,
'savemode': True,
},
'delayanalysisui': {
'geometry': (0, 0, 1280, 1024),
'mode': 'fullscreen',
'geometry': (77, 54, 960, 768),
'mode': 'normal',
'savegeometry': True,
'savemode': True,
},
Expand Down

0 comments on commit 457eb5f

Please sign in to comment.