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

final commit in branch after break

after my 1 year break in which I can not overview
the changes I try to set a new start
parent d5af5590
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
File mode changed from 100644 to 100755
import numpy as np
import math
import cv2 as cv
from skimage import color
# TODO create generic class
......@@ -9,12 +11,12 @@ class Contrast(object):
self._img = img
class LightDark(Contrast):
"""docstring for LightDark"""
class Monochromatic(Contrast):
"""docstring for Monochromatic"""
def __init__(self, img, method='luminance'):
super(LightDark, self).__init__(img)
super(Monochromatic, self).__init__(img)
meth = getattr(LightDark, method)
meth = getattr(Monochromatic, method)
self.ctrst = meth(self)
def luminance(self):
......@@ -49,6 +51,47 @@ class LightDark(Contrast):
return lightness
def redniss_lab(self):
"""Returns the redness contrast on the basis of CIE Lab
The a channel of the representation the image in CIE Lab is
isolated and the value 127 set as 0 to eliminate the green
shares of a.
"""
img = cv.cvtColor(self._img, cv.COLOR_BGR2Lab)
lab_a = img[:, :, 1].copy()
lab_a[lab_a < 128] = 0
redness = np.subtract(lab_a[lab_a > 127], 128)
return redness
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"
imglab = color.rgb2lab(imgrgb)
imglch = color.lab2lch(imglab)
value = Monochromatic(self._img, method='value')
value = value.value()
imgcv = imglch[:,:,1].astype(np.uint8)
imgcv = np.dstack((imgcv, value))
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"""
......@@ -65,24 +108,49 @@ class Saturation(Contrast):
return saturations
def chroma(self):
# CTRL stimmt vom Ergebnis noch nicht, liegt es am Rückgabedatentyp Float?
def colourfulness(self):
"""docstring for chroma"""
pass
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)
colourfulness = imglch[:, :, 1]
return colourfulness
class Hue(Contrast):
"""docstring for Saturation"""
def __init__(self, img, method='hue'):
def __init__(self, img, method='hsV'):
super(Hue, self).__init__(img)
meth = getattr(Hue, method)
self.ctrst = meth(self)
def hue(self):
"""docstring for hue"""
def hsV(self):
"""calculates the Hue value by using the HSV colorspace"""
img = cv.cvtColor(self._img, cv.COLOR_BGR2HSV)
hues = img[:, :, 0].copy()
print('hsv')
return hues
def labHcl(self) -> np.uint16:
"""calculates the Hue value by using the CIE LabHCL colorspace"""
imglab = color.rgb2lab(self._img[:,:,::-1])
imglch = color.lab2lch(imglab)
hues = imglch[:, :, 2]
vdegrees = np.vectorize(math.degrees)
hues = vdegrees(hues)
hues = np.around(hues).astype(np.uint16)
return hues
......@@ -2,6 +2,58 @@
# -*- coding: utf-8 -*-
import numpy as np
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=1):
"""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
if len(timestmp) > 2:
in_sec += int(timestmp[0]) * 3600
in_sec += int(timestmp[-2]) * 60
in_sec += int(timestmp[-1])
return in_sec
def timelabels(val, pos):
......
#!/ur/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
from pathlib import Path # TODO wie kann ich third-party module nach außen verstecken
import pickle
from subprocess import Popen, PIPE, STDOUT
# TODO Ist der import aus dem selben Pakte so korrekt?
from . import views
from . import visuals
import numpy as np
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='./'): # TODO platform independent
def __init__(self, prefix, folder='./', fps=1): # TODO platform independent
self._frames = Frames(folder, prefix)
self.fps = fps
self.fsize = self._frames.frm_cnt
# TODO Warum ist die hier?
def light_dark(self):
"""compute the light/dark contrast of the movie """
light_dark_ctrst = views.LightDark(self._frames)
return light_dark_ctrst.hist_vstack()
def showf(self, start, end=0, type='time', mltp=1, viewer='sxiv -t'):
"""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 is 'time':
start = helpers.time2framenr(start, self.fps)
end = helpers.time2framenr(end, self.fps)
elif type is 'frame':
start = int(start) * mltp
end = int(end) * mltp
# Popen sxiv with Path and image count
if '_0' in str(self._frames.frames[0]):
glob = self._frames.folder + self._frames.prefix + '{' + '{0:04d}'.format(start) + '..'\
+ '{0:04d}'.format(end) + '}' + '.png'
else:
glob = self._frames.folder + self._frames.prefix + '{' + str(start) + '..'\
+ str(end) + '}' + '.png'
cmd = viewer + ' ' + glob
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, dtype, start=1, end=0, stp=5):
"""Calculates contrast data 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
dtype: 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
Returns
-------
a view object
"""
# set movie start and end postion for the visualization
self._frames.start = start
self._frames.end = end
if dtype == '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)
return data
elif dtype in ['seqmean', 'seqmad', 'seqvar', 'seqper']:
data = views.UnivariateSequence(self._frames,)
getattr(data, dtype)(ctrst=ctrst, method=meth, frm_stp=stp)
return data
def illustrate():
"""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
......@@ -53,6 +174,7 @@ class Movie(object):
- 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
......@@ -75,6 +197,7 @@ class Movie(object):
# 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!
......@@ -181,7 +304,7 @@ class Frames(object):
def get_frame_list(self):
frm_path = Path(self.folder)
return list(frm_path.glob('*.png'))
return list(sorted(frm_path.glob('*.png')))
def count_total_frames(self):
return len(self.frames)
......
......@@ -20,7 +20,7 @@ from . import movie
class View(np.ndarray):
"""Core class for the representation of colour contrasts in Movies
View is the basis class for specific ways to represent colour contrasts.
View is the base class for specific ways to represent colour contrasts.
It does hold the definitions of contrasts itself. Objects of class View
subclass the numpy array class and hence inherit numpy methods. However,
it is not recommended to use functions which manipulate the array in
......@@ -88,7 +88,7 @@ class View(np.ndarray):
Class object which corresponds to the parameter
"""
cls_names = {'light_dark': 'LightDark',
cls_names = {'monochromatic': 'Monochromatic',
'saturation': 'Saturation',
'hue': 'Hue',
'cold-warm': 'ColdWarm',
......@@ -98,6 +98,7 @@ class View(np.ndarray):
return cls
# scheint niemals verwendet zu werden
def frms_from_indcs(self, indices):
"""results frame numbers from a list of indices"""
indices = np.add(indices, 1)
......@@ -134,7 +135,7 @@ class MultivariateSequence(View):
View.__array_finalize__(self, obj)
self._threshold = getattr(obj, '_threshold', None)
def populate(self, ctrst='light_dark', method='luminance', frm_stp=10, bins=16, thrsh=60000, start=1, end=0):
def populate(self, ctrst='monochromatic', method='luminance', frm_stp=10, bins=16, thrsh=60000, start=1, end=0):
"""doc (aus __new__ zusammentragen)
"""
......@@ -145,19 +146,30 @@ class MultivariateSequence(View):
self._bins = bins
self._threshold = thrsh
# TODO dafür müssen erst getters und setters in movie.frames definiert werden
# selef._frames.start = start
# 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):
pwd = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
# 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:04d}'.format(frm_nr) + '.png'
else:
pwd = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
img = cv2.imread(pwd) # BGR
print('ctrst: {0} and meth: {1}'.format(self._contrast, self._method))
# Kontrast Parameternamen auf Klassennamen mappen
ctrst_cls = self._get_ctrst_cls_name(self._contrast)
ctrst_img = ctrst_cls(img).ctrst
ctrst_img = ctrst_cls(img, self._method).ctrst
shape = ctrst_img.shape
ctrst_img = np.reshape(ctrst_img, (shape[0] * shape[1]))
......@@ -203,7 +215,7 @@ class BivariateSequence(View):
View.__array_finalize__(self, obj)
# das kann ich jetzt auch mit quantilen machen
def populate(self, variant='peak', ctrst='light_dark',
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
......@@ -233,7 +245,12 @@ class BivariateSequence(View):
# 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 = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
pwd = ""
if '_0' in str(self._frames.frames[0]):
pwd = self._frames.folder + self._frames.prefix + '{0:04d}'.format(frm_nr) + '.png'
else:
pwd = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
img = cv2.imread(pwd)
# BREAK: Funktioniert nicht
......@@ -357,7 +374,7 @@ class UnivariateSequence(View):
return mean, mad
# TODO: Kurve muß unbeding von cuts bereinigt werde und interpolation funktioniert da nicht
def seqmean(self, ctrst='light_dark',
def seqmean(self, ctrst='monochromatic',
method='luminance', frm_stp=10, bins=256):
"""Creates a scatterplot for the dynamic of contrast across movie frames
......@@ -383,11 +400,16 @@ class UnivariateSequence(View):
# 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 = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
pwd = ""
if '_0' in str(self._frames.frames[0]):
pwd = self._frames.folder + self._frames.prefix + '{0:04d}'.format(frm_nr) + '.png'
else:
pwd = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
img = cv2.imread(pwd)
ctrst_cls = self._get_ctrst_cls_name(self._contrast)
ctrst_img = ctrst_cls(img).ctrst
ctrst_img = ctrst_cls(img, method=self._method).ctrst
hist_value, _ = np.histogram(ctrst_img.flatten(),
bins=self._bins, range=(0, 256))
......@@ -411,7 +433,7 @@ class UnivariateSequence(View):
contrast_points = None
# TODO: Kurve muß unbeding von cuts bereinigt werde und interpolation funktioniert da nicht
def seqmad(self, ctrst='light_dark',
def seqmad(self, ctrst='monochromatic',
method='luminance', frm_stp=10, bins=256):
"""Creates a scatterplot for the dynamic of contrast across movie frames
......@@ -437,7 +459,12 @@ class UnivariateSequence(View):
# 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 = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
pwd = ""
if '_0' in str(self._frames.frames[0]):
pwd = self._frames.folder + self._frames.prefix + '{0:04d}'.format(frm_nr) + '.png'
else:
pwd = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
img = cv2.imread(pwd)
ctrst_cls = self._get_ctrst_cls_name(self._contrast)
......@@ -464,7 +491,7 @@ class UnivariateSequence(View):
self[:] = contrast_points
contrast_points = None
def seqper(self, ctrst='light_dark',
def seqper(self, ctrst='monochromatic',
method='luminance', perc=50, frm_stp=10, bins=256):
"""Creates a scatterplot for the dynamic of contrast across movie frames
......@@ -490,11 +517,16 @@ class UnivariateSequence(View):
# 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 = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
pwd = ""
if '_0' in str(self._frames.frames[0]):
pwd = self._frames.folder + self._frames.prefix + '{0:04d}'.format(frm_nr) + '.png'
else:
pwd = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
img = cv2.imread(pwd)
ctrst_cls = self._get_ctrst_cls_name(self._contrast)
ctrst_img = ctrst_cls(img).ctrst
ctrst_img = ctrst_cls(img, method=self._method).ctrst
percentile = np.percentile(ctrst_img.flatten(), perc)
......@@ -507,7 +539,7 @@ class UnivariateSequence(View):
self[:] = contrast_points
contrast_points = None
def seqvar(self, ctrst='light_dark',
def seqvar(self, ctrst='monochromatic',
method='luminance', perc=50, frm_stp=10, bins=256):
"""Creates a scatterplot for the dynamic of contrast across movie frames
......@@ -533,7 +565,12 @@ class UnivariateSequence(View):
# 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 = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
pwd = ""
if '_0' in str(self._frames.frames[0]):
pwd = self._frames.folder + self._frames.prefix + '{0:04d}'.format(frm_nr) + '.png'
else:
pwd = self._frames.folder + self._frames.prefix + str(frm_nr) + '.png'
img = cv2.imread(pwd)
ctrst_cls = self._get_ctrst_cls_name(self._contrast)
......
......@@ -140,11 +140,14 @@ class UnivariatePlot(SequencePlot):
self._ax = plt.axes()
self._ax, self._axt = self.ittenstyle(self._ax, view)
def plot(self, view, mark_gt=False, mark_lt=False, mark=False):
def plot(self, view, label=False, mark_gt=False, mark_lt=False, mark=False):
if not(label):
label = view.feature
# Interpolation mit savitzky_golay funktioniert nicht
# contrast_points = savitzky_golay(np.array(contrast_points), 51, 7)
self._axt.plot(self._x, view, label=view.feature)
self._axt.plot(self._x, view, label=label)
if any([mark, mark_gt, mark_lt]):
self._vlines(view, mark, mark_gt, mark_lt)
......
File mode changed from 100644 to 100755
# %matplotlib qt5
# %load_ext autoreload
# %autoreload 2
from itten.movie import Movie
from itten.views import MultivariateSequence
from itten.views import UnivariateSequence
from itten.visuals import MultivariatePlot
from itten.visuals import UnivariatePlot
movie = Movie(prefix='wwz_', folder='../DHd-2017/Data/Frames/WWZ/')
movie = Movie(prefix='wwz_', folder='../Dissemination/DHd-2017/Data/Frames/WWZ/')
# movie._frames.start = 367
# movie._frames.end = 1000
# cont = MultivariateSequence(movie._frames,)
cont = UnivariateSequence(movie._frames,)
cont.seqvar(frm_stp=10, ctrst='light_dark')
# cont = UnivariateSequence(movie._frames,)
# cont.seqvar(frm_stp=2, ctrst='light_dark')
# viz = MultivariatePlot(cont)
# viz.plot(cont, mark=[243, 1885, 3430, 4237, 4543])
# cont.seqper(frm_stp=5, ctrst='saturation', perc=50)
......@@ -18,3 +21,14 @@ cont.seqvar(frm_stp=10, ctrst='light_dark')
# viz = MultivariatePlot()
# fig, ax = viz.plot(cont)
# Chroma
# movie = Movie(prefix='rec_', folder='../Dissemination/DHd-2017/Data/Frames/Rec/')
# chro = UnivariateSequence(movie._frames,)
# chrom = MultivariateSequence(movie._frames)
# chro.seqmean(frm_stp=20, ctrst='saturation', method='colourfulness')
# chrom.populate(frm_stp=20, ctrst='saturation', method='colourfulness')
# viz = UnivariatePlot(chro)
# viz2 = MultivariatePlot(chrom)
# viz.plot(chro)