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

add extra classes for contrasts

rename contrasts module to views
create contrasts module
convert luminance function to method of LightDark Class
modify compose method to work with contrast module
parent a3eb2455
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TODO import as cv
import cv2
import numpy as np
from .helpers import luminance
from copy import deepcopy
# subclassing numpy ndarray
# Vorgehen: https://docs.scipy.org/doc/numpy/user/basics.subclassing.html
# resize Probleme https://sourceforge.net/p/numpy/mailman/message/12594801/
# 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
View is the basis 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
terms of structure. In this case some of the additional functions which
are implemented by this class and its subclasses might not lead to
reasonable results.
Attributes:
TODO Docstring komplettieren und Verfahren überprüfen
"""
def __new__(cls, frames, input_array=None):
"""instantiates the view class
instantiation complies with the recommendation for subclassing
numpy.ndarray
Parameters
----------
frames : itten.movie.Frames
input_array : itten.contrasts.View # TODO wie schaffte np lower case object
contrast : String # Modifizieren Channel Integer to String
frame_step : Int
savefig : Boolean
Returns
-------
Object : View
Empty numpy.ndarray of type View
"""
obj = input_array
if type(obj) == np.ndarray:
obj = np.asarray(input_array, dtype=np.uint8).view(cls).copy()
else:
input_array = np.zeros((0), dtype=np.uint8)
obj = np.asarray(input_array).view(cls).copy()
obj._frames = frames
obj._contrast = 2
obj._frame_step = 10
obj._bins = 256
return obj
import cv2 as cv
def __array_finalize__(self, obj):
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)
self._bins = getattr(obj, '_bins', None)
def __array_wrap__(self, out_arr, context=None):
return np.ndarray.__array_wrap__(self, out_arr, context)
# 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__
class VHistStack(View):
def __new__(cls, frames, input_array=None):
"""Represents a movie contrast in terms of stacked histograms
For each defined frame a histogram is calculated and each bin that
exceeds threshold is considered in the output array
Parameters
----------
bins : Int
Number of bins for the calculation of the histogram
thrsh : Int
Threshhold defining the number of pixels a bin needs to contain
to be considered in the result
Returns
-------
Object : VHistStack
VHistStack is a 2-dimensional numpy array which contains
the frame number, the bin number and a quantifier which
represents the relative weight of the bin in the frame
"""
obj = View.__new__(cls, frames, input_array=input_array)
obj._threshold = 60000
return obj
class LightDark(object):
"""docstring for LightDark"""
def __init__(self, img, method='luminance'):
self._img = img
def __array_finalize__(self, obj):
if obj is None: return
View.__array_finalize__(self, obj)
self._threshold = getattr(obj, '_threshold', None)
meth = getattr(LightDark, method)
self.ctrst = meth(self)
def luminance(self):
"""Creates light/dark values using luminance quantifiers for RGB
# TODO jetzt ausschließlich mit self numpy rechnen statt mit contrast_points liste
def populate(self, ctrst=2, frm_stp=10, bins=16, thrsh=60000, start=1, end=0):
"""doc (aus __new__ zusammentragen)
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_factors = np.array([.114, .587, .299])
# set class properties
self._contrast = ctrst
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
# slef._frames.start = start
# slef._frames.end = end
# Erzeugung eines eindimensionalen Arrays für die effizientere Berechnung
self._img = np.multiply(self._img, luminance_factors)
contrast_points = []
# 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'
# addiert alle Werte auf einer bestimmten Achse
luminances = np.sum(self._img, axis=2)
img = cv2.imread(pwd) # BGR
return luminances
# TODO Dictionary um Parameter auf Kontrast Funktion zu mappen
def value(self):
"""Creates light/dark values using the value channel in HSV"""
img = cv.cvtColor(self._img, cv.COLOR_BGR2HSV)
values = img[:, :, 2].copy()
# so wie hier für 2 das ist die Referznimplementierung
# luminances gibt ein Bild mit den luminanz werten in der
# 3. Dimension zurück
if self._contrast == 2:
luminances = luminance(img)
hist_value, _ = np.histogram(luminances.flatten(), bins=self._bins, range=(0, 256))
else:
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV_FULL)
hist_value = cv2.calcHist([img_hsv], [self._contrast], None, [16], [0, 256])
return values
for bin_index, point in enumerate(hist_value):
if point > self._threshold:
contrast_points.append((frm_nr, bin_index, int(point)))
def lightness(self):
"""Creates light/dark values using the value channel in HSV"""
img = cv.cvtColor(self._img, cv.COLOR_BGR2HLS)
lightness = img[:, :, 1].copy()
contrast_points = np.asarray(contrast_points, np.uint8)
shape = contrast_points.shape
self.resize(shape, refcheck=False)
self[:, :] = contrast_points
return lightness
return deepcopy(self) # TODO does not create a new object
......@@ -5,7 +5,7 @@ from pathlib import Path # TODO wie kann ich third-party module nach außen ver
# TODO Ist der import aus dem selben Pakte so korrekt?
from . import helpers
from . import contrasts
from . import views
# 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.
......@@ -17,7 +17,7 @@ class Movie(object):
def light_dark(self):
"""compute the light/dark contrast of the movie """
light_dark_ctrst = contrasts.LightDark(self._frames)
light_dark_ctrst = views.LightDark(self._frames)
return light_dark_ctrst.hist_vstack()
......
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# TODO import as cv
import cv2
import numpy as np
from .helpers import luminance
from copy import deepcopy
from . import contrasts
# subclassing numpy ndarray
# Vorgehen: https://docs.scipy.org/doc/numpy/user/basics.subclassing.html
# resize Probleme https://sourceforge.net/p/numpy/mailman/message/12594801/
# 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
View is the basis 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
terms of structure. In this case some of the additional functions which
are implemented by this class and its subclasses might not lead to
reasonable results.
Attributes:
TODO Docstring komplettieren und Verfahren überprüfen
"""
def __new__(cls, frames, input_array=None):
"""instantiates the view class
instantiation complies with the recommendation for subclassing
numpy.ndarray
Parameters
----------
frames : itten.movie.Frames
input_array : itten.contrasts.View # TODO wie schaffte np lower case object
contrast : String # Modifizieren Channel Integer to String
frame_step : Int
savefig : Boolean
Returns
-------
Object : View
Empty numpy.ndarray of type View
"""
obj = input_array
if type(obj) == np.ndarray:
obj = np.asarray(input_array, dtype=np.uint8).view(cls).copy()
else:
input_array = np.zeros((0), dtype=np.uint8)
obj = np.asarray(input_array).view(cls).copy()
obj._frames = frames
obj._contrast = 2
obj._frame_step = 10
obj._bins = 256
return obj
def __array_finalize__(self, obj):
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)
self._bins = getattr(obj, '_bins', None)
def __array_wrap__(self, out_arr, context=None):
return np.ndarray.__array_wrap__(self, out_arr, context)
def _get_ctrst_cls_name(self, param):
"""Returns corresponding class name for the contrast parameter value
Parameters
----------
param : String
String provided by the ctrst parameter in a call to the
populate method
Returns
-------
cls_name : Object
Class object which corresponds to the parameter
"""
cls_names = {'light_dark': 'LightDark',
'saturation': 'Saturation',
'hue': 'Hue',
'cold-warm': 'ColdWarm',
'complementary': 'Complementary'}
cls = getattr(contrasts, cls_names[param])
return cls
# 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__
class VHistStack(View):
def __new__(cls, frames, input_array=None):
"""Represents a movie contrast in terms of stacked histograms
For each defined frame a histogram is calculated and each bin that
exceeds threshold is considered in the output array
Parameters
----------
bins : Int
Number of bins for the calculation of the histogram
thrsh : Int
Threshhold defining the number of pixels a bin needs to contain
to be considered in the result
Returns
-------
Object : VHistStack
VHistStack is a 2-dimensional numpy array which contains
the frame number, the bin number and a quantifier which
represents the relative weight of the bin in the frame
"""
obj = View.__new__(cls, frames, input_array=input_array)
obj._threshold = 60000
return obj
def __array_finalize__(self, obj):
if obj is None: return
View.__array_finalize__(self, obj)
self._threshold = getattr(obj, '_threshold', None)
# TODO jetzt ausschließlich mit self numpy rechnen statt mit contrast_points liste
def populate(self, ctrst='light_dark', method='luminance', frm_stp=10, bins=16, thrsh=60000, start=1, end=0):
"""doc (aus __new__ zusammentragen)
"""
# set class properties
self._contrast = ctrst
self._method = method
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
# slef._frames.start = start
# slef._frames.end = end
contrast_points = []
# 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'
img = cv2.imread(pwd) # BGR
# TODO Dictionary um Parameter auf Kontrast Funktion zu mappen
# so wie hier für 2 das ist die Referznimplementierung
# luminances gibt ein Bild mit den luminanz werten in der
# 3. Dimension zurück
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))
# else:
# img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV_FULL)
# hist_value = cv2.calcHist([img_hsv], [self._contrast], None, [16], [0, 256])
for bin_index, point in enumerate(hist_value):
if point > self._threshold:
contrast_points.append((frm_nr, bin_index, int(point)))
contrast_points = np.asarray(contrast_points, np.uint8)
shape = contrast_points.shape
self.resize(shape, refcheck=False)
self[:, :] = contrast_points
return deepcopy(self) # TODO does not create a new object
from itten.movie import Movie
from itten.contrasts import VHistStack
from itten.views import VHistStack
movie = Movie(prefix='rec_', folder='../DHd-2017/Data/Frames/Rec/')
cont = VHistStack(movie._frames)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment