Commit aa232eca authored by Niels-Oliver Walkowski's avatar Niels-Oliver Walkowski
Browse files

refac(pkg): correct all flake8 errors

parent 11342779
......@@ -5,11 +5,12 @@ from skimage import color
# TODO create generic class
class Contrast(object):
"""Base class for color contrasts"""
def __init__(self, img):
self._img = img
class Monochromatic(Contrast):
"""docstring for Monochromatic"""
......@@ -25,10 +26,12 @@ class Monochromatic(Contrast):
The array has the same dimensions as the image. However the third
does only have the size 1 which contains the luminance value
"""
# Luminance Faktoren nach http://introcs.cs.princeton.edu/python/31datatype/luminance.py.html
# Luminance Faktoren nach
# http://introcs.cs.princeton.edu/python/31datatype/luminance.py.html
luminance_factors = np.array([.114, .587, .299])
# Erzeugung eines eindimensionalen Arrays für die effizientere Berechnung
# Erzeugung eines eindimensionalen Arrays für die effizientere
# Berechnung
self._img = np.multiply(self._img, luminance_factors)
# addiert alle Werte auf einer bestimmten Achse
......@@ -68,30 +71,31 @@ class Monochromatic(Contrast):
def colf_val_point(self):
"""calculates the most colourful value point in an image
used to identify tinting or toning images with its specific colourful
areas in very light or darker value regions
"""
# as long as opencv is used, convert RGB to RGB
imgrgb = self._img[:, :, ::-1]
assert(np.array_equal(self._img, imgrgb[:, :, ::-1])), "opencv scikit-image conversion wrong"
assert(np.array_equal(self._img,
imgrgb[:, :, ::-1])), "opencv scikit-image conversion wrong"
imglab = color.rgb2lab(imgrgb)
imglch = color.lab2lch(imglab)
value = Monochromatic(self._img, method='value')
value = value.value()
imgcv = imglch[:,:,1].astype(np.uint8)
imgcv = imglch[:, :, 1].astype(np.uint8)
imgcv = np.dstack((imgcv, value))
perc90 = np.percentile(imgcv[:,:,0], 90)
indices_colful = np.where(imgcv[:,:, 0] > perc90)
perc90 = np.percentile(imgcv[:, :, 0], 90)
indices_colful = np.where(imgcv[:, :, 0] > perc90)
colful_values = imgcv[indices_colful][:, 1]
colful_value_max = np.median(colful_values)
return np.array([[[colful_value_max]]])
class Saturation(Contrast):
"""docstring for Saturation"""
......@@ -114,7 +118,7 @@ class Saturation(Contrast):
imglab = color.rgb2lab(self._img)
imglch = color.lab2lch(imglab)
# TODO geht garnicht, weil nicht angemessen gerundet wird
# und skimage auch davor warnt
imglch = imglch.astype(np.uint16)
......@@ -144,7 +148,7 @@ class Hue(Contrast):
def labHcl(self) -> np.uint16:
"""calculates the Hue value by using the CIE LabHCL colorspace"""
imglab = color.rgb2lab(self._img[:,:,::-1])
imglab = color.rgb2lab(self._img[:, :, ::-1])
imglch = color.lab2lch(imglab)
hues = imglch[:, :, 2]
......
......@@ -3,48 +3,48 @@
import numpy as np
import pandas as pd
import math
# import math
# def lab2hc(labpixel, out='both'):
# """returns hue and colorfulness of a pixel
#
#
# the image pixel has to have CIE Lab colors.
# the conversion follows Fairchild's 2013 conversion equations
#
#
# labpixel : pixel from image using CIE Lab color model
# out : 'h' (hue), 'c' (colorfulness) or 'both' (tuple containing hue
# and colorfulness : default)
# """
#
#
# def get_hue(a, b):
# hue = math.degrees(math.atan(b/a))
# return hue
#
#
# def get_colorfulness(a, b):
# colorfulness = (a**2 + b**2)**(1/2)
# return colorfulness
#
#
# a = labpixel[1]
# b = labpixel[2]
#
#
# hc = {}
#
#
# if not(out == 'c'):
# hc['h'] = get_hue(a, b)
# elif not(out == 'h'):
# hc['c'] = get_colorfulness(a, b)
#
#
# return hc
# TODO fps muss noch implementiert werden
def time2framenr(time, fps=4):
"""counts the frame index for a given timestamp
time : timestamp needs to have the form [HH:]MM:SS
fps : frequency with which frames were taken from the movie
"""
timestmp = time.split(':')
in_sec = 0
......@@ -54,16 +54,17 @@ def time2framenr(time, fps=4):
in_sec += int(timestmp[-2]) * 60
in_sec += int(timestmp[-1])
return in_sec * fps
return in_sec * fps
def frame2time(val, fps=4):
"""transforms framenumbers into timecodes
Arguments:
val {int} -- number of frame in the movie
fps {int} -- time interval by which frames were extracted from the movie
fps {int} -- time interval by which frames were extracted from the
movie
Returns:
np.datetime64 -- timecode of the frame in the movie
"""
......@@ -72,13 +73,15 @@ def frame2time(val, fps=4):
tstmp = pd.Timedelta(val, unit='s')
return tstmp
def luminance(img):
"""Creates an array of luminance value for each pixel of an image
The array has the same dimensions as the image. However the third
does only have the size 1 which contains the luminance value
"""
# Luminance Faktoren nach http://introcs.cs.princeton.edu/python/31datatype/luminance.py.html
# Luminance Faktoren nach
# http://introcs.cs.princeton.edu/python/31datatype/luminance.py.html
luminance_factors = np.array([.299, .587, .114])
# Erzeugung eines eindimensionalen Arrays für die effizientere Berechnung
......
......@@ -4,30 +4,32 @@
import numpy as np
import pandas as pd
from pathlib import Path # TODO wie kann ich third-party module nach außen verstecken
from pathlib import Path
import pickle
from subprocess import Popen, PIPE, STDOUT
# TODO Ist der import aus dem selben Pakte so korrekt?
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
# 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
fps {int} -- number of frames per second that were extracted from
the movie (default: {4}) # before this were 1
"""
self._frames = Frames(folder, prefix)
......@@ -49,18 +51,20 @@ class Movie(object):
"""
# transform time to frames if type is time
if type is 'time':
if type == 'time':
start = helpers.time2framenr(start, self.fps)
end = helpers.time2framenr(end, self.fps)
elif type is 'frame':
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 = 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')
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)
......@@ -138,13 +142,13 @@ class Movie(object):
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
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
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
......@@ -157,7 +161,7 @@ class Movie(object):
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)
dtype=np.int64)
data['time'] = data['frame'].apply(helpers.frame2time, fps=4)
return data
......@@ -234,7 +238,8 @@ class Movie(object):
vis = visuals.MultivariatePlot(multivariate)
vis.plot(multivariate)
header = prefix[:-1] + ' MultivariatePlot for ' + ctrst + ' - ' + desc
header = prefix[:-1] + ' MultivariatePlot for ' + ctrst + ' - '
header += desc
filename = prefix + 'multivariateplot_' + ctrst + '_' + desc
vis.saveplt(fname=filename, title=header)
......@@ -251,7 +256,8 @@ class Movie(object):
vis = visuals.UnivariatePlot(univariate)
# summarizing methods of univariate class
summarizations = ['seqmean', 'seqmad'] # TODO Varianz funktioniert so nicht
# TODO Varianz funktioniert so nicht
summarizations = ['seqmean', 'seqmad']
for feature in summarizations:
# compute summarizations for given univariate value
......@@ -275,8 +281,10 @@ class Movie(object):
vis.plot(univariate)
# save univariate plot
header = prefix[:-1] + ' UnivariatePlot for ' + ctrst + ' - ' + desc
filename = self._frames.prefix + 'univariateplot_' + ctrst + '_' + desc
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:
......@@ -332,9 +340,10 @@ class Frames(object):
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
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')))
......
......@@ -5,11 +5,11 @@
# TODO import as cv
import cv2
import numpy as np
import peakutils
from .helpers import luminance
# import peakutils
# from .helpers import luminance
from copy import deepcopy
from . import contrasts
from . import movie
# from . import movie
# subclassing numpy ndarray
# Vorgehen: https://docs.scipy.org/doc/numpy/user/basics.subclassing.html
......@@ -17,6 +17,8 @@ from . import movie
# andere ownership Probleme könne angeblich mit out= gelöst werden
# "Use __new__ when you need to control the creation of a new instance.
# Use __init__ when you need to control initialization of a new instance."
class View(np.ndarray):
"""Core class for the representation of colour contrasts in Movies
......@@ -41,7 +43,8 @@ class View(np.ndarray):
Parameters
----------
frames : itten.movie.Frames
input_array : itten.contrasts.View # TODO wie schaffte np lower case object
input_array : itten.contrasts.View # TODO wie schaffte np lower
case object
contrast : String # Modifizieren Channel Integer to String
frame_step : Int
savefig : Boolean
......@@ -64,7 +67,8 @@ class View(np.ndarray):
return obj
def __array_finalize__(self, obj):
if obj is None: return
if obj is None:
return
self._frames = getattr(obj, '_frames', None)
self._contrast = getattr(obj, '_contrast', None)
self._frame_step = getattr(obj, '_frame_step', None)
......@@ -106,8 +110,12 @@ class View(np.ndarray):
frm_nrs = np.subtract(frm_nrs, self._frame_step)
return frm_nrs.flatten()
# subclassing subclass of numpy http://stackoverflow.com/questions/7342637/how-to-subclass-a-subclass-of-numpy-ndarray
# TODO es gibt noch das Problem, dass numpy nach mehreren Berechnungen von drive eine max recursion Warnung ausgiebt, warum? Brauche ich __del__
# subclassing subclass of numpyhttp://stackoverflow.com/questions/7342637/
# how-to-subclass-a-subclass-of-numpy-ndarray
# TODO es gibt noch das Problem, dass numpy nach mehreren Berechnungen von
# drive eine max recursion Warnung ausgiebt, warum? Brauche ich __del__
class MultivariateSequence(View):
def __new__(cls, frames, input_array=None):
"""Represents a movie contrast in terms of stacked histograms
......@@ -117,12 +125,12 @@ class MultivariateSequence(View):
Parameters
----------
TODO threshold : the minimum number of pixels that need to appear in
TODO threshold : the minimum number of pixels that need to appear
in a certain bin so that the bin will be included in the output
data. (Needs to be modifiable in relation to the total number of
pixels in the frame, that means on the frame size 240p60 etc.)
Oder Threshold ganz raus, weil das eigentlich eine Sache der
Visualisierung ist auszusortieren.
data. (Needs to be modifiable in relation to the total number
of pixels in the frame, that means on the frame size 240p60
etc.) Oder Threshold ganz raus, weil das eigentlich eine Sache
der Visualisierung ist auszusortieren.
Returns
-------
......@@ -137,11 +145,13 @@ class MultivariateSequence(View):
return obj
def __array_finalize__(self, obj):
if obj is None: return
if obj is None:
return
View.__array_finalize__(self, obj)
self._threshold = getattr(obj, '_threshold', None)
def populate(self, ctrst='monochromatic', method='luminance', frm_stp=10, bins=16, thrsh=6000, start=1, end=0):
def populate(self, ctrst='monochromatic', method='luminance', frm_stp=10,
bins=16, thrsh=6000, start=1, end=0):
"""doc (aus __new__ zusammentragen)
"""
......@@ -151,23 +161,27 @@ class MultivariateSequence(View):
self._frame_step = frm_stp
self._bins = bins
self._threshold = thrsh
# TODO dafür müssen erst getters und setters in movie.frames definiert werden
# TODO dafür müssen erst getters und setters in movie.frames definiert
# werden
# self._frames.start = start
# self._frames.end = end
contrast_points = np.empty((0, 3), dtype=np.uint32)
# pwd list sollte in Frames sein und hier nur durchlaufen werden
for frm_nr in range(self._frames.start, self._frames.end, self._frame_step):
for frm_nr in range(self._frames.start, self._frames.end,
self._frame_step):
# FIX for legacy numbering scheme of files 1 instead of 0001
# TODO remove this ridiculous fix and the whole pwd building which
# now solved by having implemented sorted(Path()…) in Frames
pwd = ""
if '_0' in str(self._frames.frames[0]):
pwd = self._frames.folder + self._frames.prefix + '{0:05d}'.format(frm_nr) + '.png'
pwd = self._frames.folder + self._frames.prefix
pwd += '{0:05d}'.format(frm_nr) + '.png'
else:
pwd = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
pwd = self._frames.folder + self._frames.prefix + str(frm_nr)
pwd += '.png'
img = cv2.imread(pwd) # BGR
......@@ -183,10 +197,12 @@ class MultivariateSequence(View):
for bin_index, point in enumerate(hist_value):
if int(point) > self._threshold:
entry = np.array([[frm_nr, bin_index, point]], dtype=np.uint32)
entry = np.array([[frm_nr, bin_index, point]],
dtype=np.uint32)
contrast_points = np.vstack((contrast_points, entry))
# irgendwie prüfen, ob ich contrast_points insgesamt durch self ersetzen kann
# irgendwie prüfen, ob ich contrast_points insgesamt durch self
# ersetzen kann
contrast_points = np.asarray(contrast_points, np.uint32)
shape = contrast_points.shape
self.resize(shape, refcheck=False)
......@@ -196,150 +212,6 @@ class MultivariateSequence(View):
return deepcopy(self) # TODO does not create a new object
class BivariateSequence(View):
def __new__(cls, frames, input_array=None):
"""Represents a movie contrast in terms minbound maxbound values
For each defined frame a histogram is calculated and each bin that
exceeds threshold is considered in the output array
Parameters
----------
Returns
-------
Object : BivariateSequence
"""
obj = View.__new__(cls, frames, input_array=input_array)
return obj
def __array_finalize__(self, obj):
if obj is None: return
View.__array_finalize__(self, obj)
# das kann ich jetzt auch mit quantilen machen
def populate(self, variant='peak', ctrst='monochromatic',
method='luminance', frm_stp=10, bins=256):
"""Creates a scatterplot for the dynamic of contrast across movie frames
variant: peak or bin (one of max/min peak; uper/lower mean)
threshold: for the peak variant TODO
smooth: smooth plot TODO
"""
# set class properties
self._variant = variant
self._contrast = ctrst
self._method = method
self._frame_step = frm_stp
self._bins = bins
# TODO um start und end in der Methode zu parametrisieren müssen erst
# getters und setters in movie.frames definiert werden
# self._frames.start = start
# self._frames.end = end
contrast_points = np.empty((0, 2), dtype=np.uint32)
# sofern kein oder nur ein Peak gefunden wird, man könnte dann auch
# noch einen Durchlauf mit geringeren thres und min_dist versuceh
lastmin = 0
lastmax = 256
# pwd list sollte in Frames sein und hier nur durchlaufen werden
for frm_nr in range(self._frames.start, self._frames.end,
self._frame_step):
pwd = ""
if '_0' in str(self._frames.frames[0]):
pwd = self._frames.folder + self._frames.prefix + '{0:05d}'.format(frm_nr) + '.png'
else:
pwd = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
img = cv2.imread(pwd)
# BREAK: Funktioniert nicht
if method == 'bin':
hist_values = cv2.calcHist([img_hsv], [self._channel], None,
[256], [0, 256])
hist_mins = hist_values[0:127].flatten().tolist()
hist_maxs = hist_values[128:256].flatten().tolist()
# Für jeden Frame wird jeder Kontrastwert mit der Anzahl der
# Pixel für diesen Kontrastwert multipliziert
# So wird der gesamte Kontrastwert des Frames sowie errechnet
bin_total = 0
total_value = 0
for bin_id, points in enumerate(hist_mins):
bin_total = (bin_id + 1) * points
total_value += bin_total
sum_mins = int(sum(hist_mins))
sum_maxs = int(sum(hist_maxs))
if int(total_value) == 0:
total_value = [(bin_nr, value) for bin_nr, value in
enumerate(hist_maxs) if
int(value) > 0][0][0]
mins.append(total_value)
else:
mins.append(int(total_value / sum_mins))
bin_total = 0
total_value = 0
for bin_id, points in enumerate(hist_maxs):
bin_total = (bin_id + 1) * points
total_value += bin_total
# if there are no values in hist_max take highes bin with value
# from hist_mins
if int(total_value) == 0:
total_value = [(bin_nr, value) for bin_nr, value in
enumerate(hist_mins) if
int(value) > 0][-1][0]
maxs.append(total_value)
else:
maxs.append(int(total_value / sum(hist_maxs)) + 127)
# peak variant
else:
ctrst_cls = self._get_ctrst_cls_name(self._contrast)
ctrst_img = ctrst_cls(img).ctrst
hist_value, _ = np.histogram(ctrst_img.flatten(),
bins=self._bins, range=(0, 256))
peaks = peakutils.indexes(hist_value.flatten(), thres=0.2,
min_dist=15)
# Abfangen von von nur 1 oder keinem Peak
if len(peaks) > 1:
lastmin = peaks[0]
lastmax = peaks[-1]
contrast_points = np.vstack((contrast_points,
[lastmin, lastmax]))
elif len(peaks) == 1:
# je nachdem ob der neue peak dem alten min oder max wert
# näher liegt wird er dem einen oder Anderen zugeschlagen
if (lastmax - peaks[0]) < (peaks[0] - lastmin):
lastmax = peaks[0]
contrast_points = np.vstack((contrast_points,
[lastmin, lastmax]))
else:
lastmin = peaks[0]
contrast_points = np.vstack((contrast_points,
[lastmin, lastmax]))
else:
contrast_points = np.vstack((contrast_points,
[lastmin, lastmax]))
contrast_points = np.asarray(contrast_points, np.uint32)
shape = contrast_points.shape
self.resize(shape, refcheck=False)
self[:, :] = contrast_points
contrast_points = None
class UnivariateSequence(View):
def __new__(cls, frames, input_array=None):
"""Represents a movie contrast in terms of a single feature
......@@ -352,16 +224,18 @@ class UnivariateSequence(View):
Object : BivariateSequence
"""
obj = View.__new__(cls, frames, input_array=input_array)
obj = View.__new__(cls, frames, input_array)
obj.feature = None
return obj
def __array_finalize__(self, obj):
if obj is None: return
if obj is None:
return
self._threshold = getattr(obj, 'feature', None)
View.__array_finalize__(self, obj)
# TODO später ersetzen durch numpy mean/deviation in Kombination mit anderen Möglichkeiten
# TODO später ersetzen durch numpy mean/deviation in Kombination mit
# anderen Möglichkeiten
@staticmethod
def meanmad(values):
......@@ -370,14 +244,15 @@ class UnivariateSequence(View):
points = np.sum(values)
# range nicht array und histogram verschachtelt (siehe slice)
bin_values = np.apply_along_axis(lambda x: x * values[x-1], 0,
range(1, length+1))
range(1, length+1))
mean = np.sum(bin_values) / points
absolutes = np.apply_along_axis(lambda x: abs(x - mean) * values[x-1],
0, range(1, length+1))