movie.py 7.15 KB
Newer Older
1
#!/ur/bin/env python
2
3
4
# -*- coding: utf-8 -*-

from pathlib import Path  # TODO wie kann ich third-party module nach außen verstecken
5
import pickle
6
7

# TODO Ist der import aus dem selben Pakte so korrekt?
8
from . import views
9
10
11
from . import visuals
import numpy as np

12

13
14
# 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.
15
16
17
class Movie(object):
    """main class to interact with the colorspace of movieframes"""
    def __init__(self, prefix, folder='./'):  # TODO platform independent
18
19
        self._frames = Frames(folder, prefix)
        self.fsize = self._frames.frm_cnt
20

21
    # TODO Warum ist die hier?
22
23
    def light_dark(self):
        """compute the light/dark contrast of the movie """
24
        light_dark_ctrst = views.LightDark(self._frames)
25
        return light_dark_ctrst.hist_vstack()
26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
    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
        """
        # 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)
78
            multivariate.populate(ctrst=ctrst, frm_stp=stp+2)
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

            # TODO: pickle instance instead of just deleating it!

            # plot multivariate view
            vis = visuals.MultivariatePlot(multivariate)
            vis.plot(multivariate)

            header = prefix[:-1] + ' MultivariatePlot for ' + ctrst + ' - ' + 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
103
            summarizations = ['seqmean', 'seqmad']  # TODO Varianz funktioniert so nicht
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

            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 + ' - ' + desc
            filename = self._frames.prefix + 'univariateplot_' + ctrst + '_' + desc
            vis.saveplt(fname=filename, title=header)

131
132
133
        with open(prefix[:-1] + '_summary.pickle', 'wb') as f:
            pickle.dump(summary, f)

134
135
        return summary

136
137

class Frames(object):
138
    # TODO getters und setters für start und end setzen
139
    # TODO frm_stp sollte definitiv Teil der Frames Klasse werden
140
141
142
143
144
145
146
    """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
147
148
149
        self.end = end
        self.frm_cnt = 0

150
151
152
153
    # TODO start als property erzeugt bisher eine Endlosschleife wei
    # count_frames sart und end brauchen und so gegenseitig sart und end nicht
    # gesetzt wird

154
155
156
157
158
159
160
161
    @property
    def end(self):
        return self.__end

    @end.setter
    def end(self, nr):
        if nr == 0:
            self.__end = self.frm_length
162
            self.frm_cnt = self.count_frames()
163
164
        elif nr > self.frm_length:
            self.__end = self.frm_length - 1
165
            self.frm_cnt = self.count_frames()
166
167
        elif nr < self.start:
            self.__end = self.start + 1
168
            self.frm_cnt = self.count_frames()
169
170
        else:
            self.__end = nr
171
            self.frm_cnt = self.count_frames()
172
173
174
175
176
177
178
179
180

    @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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

    def get_frame_list(self):
        frm_path = Path(self.folder)
        return list(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