#!/ur/bin/env python # -*- coding: utf-8 -*- import numpy as np import pandas as pd from pathlib import Path import pickle from subprocess import Popen, PIPE, STDOUT from . import views from . import visuals from . import helpers # zum Problem mit privaten und öffentlichen Eigenschaften # http://www.python-course.eu/python3_properties.php und 'Fluent Python' # relativ weit vorne. # numpy gibt beim Versuch zB. size zu schreiben auch ein AttributeError() aus. class Movie(object): """main class to interact with the colorspace of movieframes""" def __init__(self, prefix, folder='./', fps=4): """creates an video object holding the frames of the video Arguments: object {self} -- the video object itself prefix {str} -- part of the frames filenames representing the movie Keyword Arguments: folder {str} -- folder containing the frames (default: {'./'}) fps {int} -- number of frames per second that were extracted from the movie (default: {4}) # before this were 1 """ self._frames = Frames(folder, prefix) self.fps = fps self.fsize = self._frames.frm_cnt def showf(self, start, end=0, type='time', mltp=1, viewer='eog'): """Opens frames within the given timespan in an image viewer start : timestamp of the start position [hh:]mm:ss or frame number as a string end : timestamp of the end position [hh:]mm:ss or frame number as a string type : measure type of from and to. Values 'time' or 'frame' or list mltp : use a different multiplyer to multiply frame number than the frame step of the Movie instance viewer : image view of choiche (defaults to sxiv) """ # transform time to frames if type is time if type == 'time': start = helpers.time2framenr(start, self.fps) end = helpers.time2framenr(end, self.fps) elif type == 'frame': start = int(start) * mltp end = int(end) * mltp # funktioniert nur in zsh # glob = self._frames.folder + self._frames.prefix + '{' + '{0:05d}'. # format(start) + '..' + '{0:05d}'.format(end) + '}' + '.png' glob_lst = [] for frm in range(start, end+1): glob_lst.append(self._frames.folder + self._frames.prefix + '{0:05d}'.format(frm) + '.png') cmd = viewer + ' ' + ' '.join(glob_lst) Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=True) return True def visualize(self, ctrst, meth, vtype, start=1, end=0, stp=5, save=False, desc=False): """Creates a visualization for a specific contrast in the movie Parameters ---------- ctrst: the contrast to be plotted method: the method which should be used by the contrast class in order to calculate the contrast vtype: type of the visualization (distribution, seqmean, seqmad, etc) start: Int which defines the start frame for the description end: Int which defines the end frame for the description (0 defines the unknown end frame) stp: the frequency in sec by which frames are used to calculate the contrast save: wether the plot should be saved to a file or not TODO desc: a description which is used as a title and filename for the visualization Returns ------- a tuple containing a view and a matplotlib object """ # set default plot description if no description is provided if not(desc): desc = 'Frame ' + str(start) + ' bis ' + str(end) data = self.analyze(ctrst, meth, vtype, start, end, stp) # build visualization for the selected visualization type if vtype == 'distribution': vis = visuals.MultivariatePlot(data) elif vtype in ['seqmean', 'seqmad', 'seqvar', 'seqper']: vis = visuals.UnivariatePlot(data) vis.plot(data) return data, vis # export plot to file if save: filename = self._frames.prefix + vtype + 'plot' + '_' + ctrst + \ '_' + desc header = self._frames.prefix + vtype + 'plot' + ' for ' + \ ctrst + ' - ' + desc vis.saveplt(fname=filename, title=header) def analyze(self, ctrst, meth, view, start=1, end=0, stp=5): """Calculates contrast data for a specific contrast in the movie Parameters ---------- ctrst: the contrast to be plotted possible values are: monochromatic, saturation, hue, cold-warm, complementary method: the method by which the peculiar contrast is calculated. Possible values depend on the contrast type and include: luminance, value, lightness, redniss_lab (monochromatic) saturation, colourfulness (saturation) hsV, labHcl (hue) view: the way contrast is modeled in the data possible values are: distribution, seqmean, seqmad, seqvar, seqper start: Int which defines the start frame for the description end: Int which defines the end frame for the description (0 defines the unknown end frame) stp: the frequency in frame number by which frames are used to calculate the contrast Returns ------- TODO: a pandas DataFrame containing the frame number, timestamp, bin and number of pixels contained in the bin """ # set movie start and end postion for the visualization self._frames.start = start self._frames.end = end if view == 'distribution': # CTRL funktioniert start/end hier wenn ich es nicht noch einmal # mit übergebe ? data = views.MultivariateSequence(self._frames,) data.populate(ctrst=ctrst, method=meth, frm_stp=stp) data = pd.DataFrame(data, columns=['frame', 'bin', 'pixels'], dtype=np.int64) data['time'] = data['frame'].apply(helpers.frame2time, fps=4) return data elif view in ['seqmean', 'seqmad', 'seqvar', 'seqper']: data = views.UnivariateSequence(self._frames,) getattr(data, view)(ctrst=ctrst, method=meth, frm_stp=stp) return data def illustrate(self): """Visualizes the value of a contrast on frame images """ pass def describe(self, desc=False, start=1, end=0, stp=5): """Calculates each feature provided by Itten for the movie instance Summary values will be returned in a python dictionary. Contrast sequences will not be returned due to save memory. The method which will be used to compute each contrast is the contrast class' default method. Instead the will be plotted using Itten Sequence Plots. More precisely MultivariatePlot and UnivariatePlot will be stored on disk for each contrast type. The filename follows the Pattern prefix + contrast. Parameters ---------- start: Int which defines the start frame for the description end: Int which defines the end frame for the description (0 defines the unknown end frame) Returns ------- Dictionary: Dictionary which contains summarizing statistics The Dictionary keys carry the names of the type of summarization. For each contrast it contains: min, max, mean, median, 25, 75, standard deviation, variance. And for each of this values the same set of values are computed. ToDo ---- - outsource summary/feature loop to function which can be accessed by the view class - pickle view data - visualize und derive integrieren und dann entsprechend aussortieren """ # set properties of the current function call self._frames.start = start self._frames.end = end prefix = self._frames.prefix if not(desc): desc = 'Frame ' + str(start) + ' bis ' + str(end) # Summary Dictionary title = prefix[:-1] summary = {title: {}} # available contrasts contrasts = ['saturation', 'light_dark'] # compute all available statistics for each contrast for ctrst in contrasts: summary[title][ctrst] = {} # compute multivariatee contrast representation multivariate = views.MultivariateSequence(self._frames) # frm_stp wird um 2 erhöht weil es sonst scheiße aussieht multivariate.populate(ctrst=ctrst, frm_stp=stp+2) # TODO: pickle instance instead of just deleating it! # plot multivariate view vis = visuals.MultivariatePlot(multivariate) vis.plot(multivariate) header = prefix[:-1] + ' MultivariatePlot for ' + ctrst + ' - ' header += desc filename = prefix + 'multivariateplot_' + ctrst + '_' + desc vis.saveplt(fname=filename, title=header) # compute summarizations for current contrast univariate = views.UnivariateSequence(self._frames) # TODO Workaround, becaus frm_stp is not part of Frames classbut # view class so when I instantiate vis below it defaults to the # __init__ value 10 and not the value set by the call of describe. # This leads to a matplotlib error univariate._frame_step = stp # instantiate plot for all univariate summarizations vis = visuals.UnivariatePlot(univariate) # summarizing methods of univariate class # TODO Varianz funktioniert so nicht summarizations = ['seqmean', 'seqmad'] for feature in summarizations: # compute summarizations for given univariate value getattr(univariate, feature)(ctrst=ctrst, frm_stp=stp) # TODO Pickle current instance state # describe current feature by summarizing statistics summary[title][ctrst][feature[3:]] = { 'minv': int(univariate.min()), 'maxv': int(univariate.max()), 'mean': int(univariate.mean()), 'median': int(np.median(univariate)), 'perc25': int(np.percentile(univariate, 25)), 'perc75': int(np.percentile(univariate, 75)), 'std': int(univariate.std()), 'var': int(univariate.var()) } # plot current summarization current univariate contrast plot vis.plot(univariate) # save univariate plot header = prefix[:-1] + ' UnivariatePlot for ' + ctrst + ' - ' header += desc filename = self._frames.prefix + 'univariateplot_' + ctrst + '_' filename += desc vis.saveplt(fname=filename, title=header) with open(prefix[:-1] + '_summary.pickle', 'wb') as f: pickle.dump(summary, f) return summary class Frames(object): # TODO getters und setters für start und end setzen # TODO frm_stp sollte definitiv Teil der Frames Klasse werden """Parses movie frames properties""" def __init__(self, folder, prefix, start=1, end=0): self.folder = folder self.prefix = prefix self.frames = self.get_frame_list() self.frm_length = self.count_total_frames() self.start = start self.end = end self.frm_cnt = 0 # TODO start als property erzeugt bisher eine Endlosschleife wei # count_frames sart und end brauchen und so gegenseitig sart und end nicht # gesetzt wird @property def end(self): return self.__end @end.setter def end(self, nr): if nr == 0: self.__end = self.frm_length self.frm_cnt = self.count_frames() elif nr > self.frm_length: self.__end = self.frm_length - 1 self.frm_cnt = self.count_frames() elif nr < self.start: self.__end = self.start + 1 self.frm_cnt = self.count_frames() else: self.__end = nr self.frm_cnt = self.count_frames() @property def frm_cnt(self): return self.__frm_cnt @frm_cnt.setter def frm_cnt(self, nr): count = self.count_frames() self.__frm_cnt = count def get_frame_list(self): """returns the list of paths for all frame files in the current folder Returns: list -- returns the list of paths for all frame files in the current folder """ frm_path = Path(self.folder) return list(sorted(frm_path.glob('*.png'))) def count_total_frames(self): return len(self.frames) def count_frames(self): return self.end - (self.start - 1) def _get_end_frame(self, end): if end == 0: return self.frm_length else: return end