diff --git a/setup.py b/setup.py
index 2176054..c42357c 100644
--- a/setup.py
+++ b/setup.py
@@ -120,7 +120,7 @@ def get_version():
def get_install_requires():
if 'FLATPAK_INSTALL' in os.environ:
- install_requires=['cycler', 'matplotlib', 'numpy',
+ install_requires=['cycler', 'matplotlib', 'numpy==1.26.4',
'py', 'pyparsing', 'pyqt5', 'pytest',
'python-dateutil', 'pytz', 'six',
'scipy',
diff --git a/src/installer.cfg b/src/installer.cfg
index a3cfc10..a2630d2 100644
--- a/src/installer.cfg
+++ b/src/installer.cfg
@@ -1,7 +1,7 @@
[Application]
name=rdplot
publisher=IENT
-version=1.0
+version=1.3.4
# How to launch the app - this calls the 'main' function from the 'myapp' package:
entry_point=rdplot.__main__:main
icon=rdplot/logo/plot512_0wd_icon.ico
diff --git a/src/rdplot/SimulationDataItem.py b/src/rdplot/SimulationDataItem.py
index 36d25be..8820714 100644
--- a/src/rdplot/SimulationDataItem.py
+++ b/src/rdplot/SimulationDataItem.py
@@ -25,7 +25,7 @@
from os import listdir
from os.path import join, abspath, isfile, isdir, basename, splitext
from PyQt5.QtCore import *
-from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QComboBox, QPushButton, QDialogButtonBox, QLabel, QCheckBox, QGroupBox, QMessageBox, QApplication
+from PyQt5.QtWidgets import QDialog, QVBoxLayout, QComboBox, QDialogButtonBox, QLabel, QCheckBox
import re
@@ -457,7 +457,7 @@ def create_item_list_from_directory(self, directory_path):
except SimulationDataItemError as error:
pass
# We definitely cannot accept thousands of exceptions on the command line
- # print((AbstractEncLog
+ # print((
# "Could not create simulation data item from file '{}'"
# " due to {}"
# ).format(path, error))
@@ -522,7 +522,7 @@ def parse_csv_item_list(self, log_path):
continue
item_list.append(CSVLog(config, header, line))
return item_list
- except:
+ except Exception as e:
raise SimulationDataItemError()
# Magic Methods
diff --git a/src/rdplot/SimulationDataItemClasses/CsvLogs.py b/src/rdplot/SimulationDataItemClasses/CsvLogs.py
index 2668bd4..58c1675 100644
--- a/src/rdplot/SimulationDataItemClasses/CsvLogs.py
+++ b/src/rdplot/SimulationDataItemClasses/CsvLogs.py
@@ -32,7 +32,15 @@ def __init__(self, config, header, line):
header = header.replace("\n", "")
header = re.split(r'[,;]',header.lower())
header = list(filter(None, header))
- sequence_idx = header.index("sequence")
+
+ try:
+ sequence_idx = header.index("sequence")
+ except Exception: # Sequence not found. Search for partial string
+ sequence_idx = 0
+ for i, token in enumerate(header):
+ if "sequence" in token:
+ sequence_idx = i
+ break
try:
qp_idx = header.index("qp")
diff --git a/src/rdplot/SimulationDataItemClasses/DatLogs.py b/src/rdplot/SimulationDataItemClasses/DatLogs.py
index 9220e1f..4193a7a 100644
--- a/src/rdplot/SimulationDataItemClasses/DatLogs.py
+++ b/src/rdplot/SimulationDataItemClasses/DatLogs.py
@@ -20,7 +20,7 @@
import xmltodict
from abc import abstractmethod
from xml.parsers.expat import ExpatError
-from os.path import normpath, basename, sep, dirname
+from os.path import normpath, dirname
from rdplot.SimulationDataItem import (AbstractSimulationDataItem, SimulationDataItemError)
diff --git a/src/rdplot/SimulationDataItemClasses/EncoderLogs.py b/src/rdplot/SimulationDataItemClasses/EncoderLogs.py
index 9f59816..771fb2b 100644
--- a/src/rdplot/SimulationDataItemClasses/EncoderLogs.py
+++ b/src/rdplot/SimulationDataItemClasses/EncoderLogs.py
@@ -259,7 +259,6 @@ def _parse_summary_data(self):
vals = [float(val) for val in vals] # convert to numbers
name_val_dict = dict(zip(names, vals)) # pack both together in a dict
- # print(summary_type)
name_rate = 'Bitrate'
if summary_type == 'SUMMARY':
diff --git a/src/rdplot/Widgets/MainWindow.py b/src/rdplot/Widgets/MainWindow.py
index c839f29..47a719a 100644
--- a/src/rdplot/Widgets/MainWindow.py
+++ b/src/rdplot/Widgets/MainWindow.py
@@ -246,6 +246,7 @@ def remove(self):
self._selection_model.selectionChanged.connect(self.change_list)
self._variable_tree_selection_model.selectionChanged.connect(self.update_plot)
if len(self.selectedSimulationDataItemListModel.values()) == 0:
+ self.plotPreview.reset_plot_color_cycle()
self.update_plot()
def change_list(self, q_selected, q_deselected):
@@ -396,7 +397,7 @@ def check_labels(self):
selectionmodel.clearSelection()
# updates the plot if the plot variable is changed
- def update_plot(self, force=False):
+ def update_plot(self, force=True):
# user-generated curves and curves loaded from files are not supposed to be mixed
user_generated_curves = False
if self.sender() == self._variable_tree_selection_model or self.sender() == self.curveListSelectionModel or force:
diff --git a/src/rdplot/Widgets/PlotWidget.py b/src/rdplot/Widgets/PlotWidget.py
index 0152312..a6c267a 100644
--- a/src/rdplot/Widgets/PlotWidget.py
+++ b/src/rdplot/Widgets/PlotWidget.py
@@ -86,12 +86,22 @@ def __init__(self, ):
self.anchor_identifier = ''
self.ci_mode = 'average'
+ self.reset_plot_color_cycle()
+
+ self.ci_visible = False
+
+ def reset_plot_color_cycle(self):
+ '''
+ Reset the linestyles and color cycle for plotting
+ '''
self.color_cycle = ['r', 'b', 'y', 'k', 'c', 'm', 'g', 'r', 'b', 'y', 'k', 'c', 'm', 'g']
- self.marker_cycle = ['x', 'x', 'x', 'x', 'x', 'x', 'x', 'o', 'o', 'o', 'o', 'o', 'o', 'o']
+ self.marker_cycle = ['x', 'o', 'v', '2', 's', 'P', '*', 'D', 'h']
self.linestyle_cycle = ["-", "--", ":", "-."]
self.plot_index = 0
+ self.marker_index = 0
self.plot_linestyle_index = 0
self.color_list = []
+ self.marker_list = []
self.linestyle_list = [
("psnr y", self.linestyle_cycle[0]), ("psnr u", self.linestyle_cycle[1]), ("psnr v", self.linestyle_cycle[2]),
("wpsnr y", self.linestyle_cycle[0]), ("wpsnr u", self.linestyle_cycle[1]), ("wpsnr v", self.linestyle_cycle[2]),
@@ -100,9 +110,6 @@ def __init__(self, ):
("mos", self.linestyle_cycle[0]),
]
- self.ci_visible = False
-
-
def create_legend(self, plot_data_collection):
tmp_legend = []
for plot_data in plot_data_collection:
@@ -121,20 +128,35 @@ def create_legend(self, plot_data_collection):
return legend
-
- def set_color(self, name):
+ def set_color(self, name, sequence):
+ color = None
for color_item in self.color_list:
if color_item[0] == name:
- return (color_item[1], color_item[2])
+ color = color_item[1]
+
+ marker = None
+ for marker_item in self.marker_list:
+ if marker_item[0] == sequence:
+ marker = marker_item[1]
- color = self.color_cycle[self.plot_index]
- marker = self.marker_cycle[self.plot_index]
+ if color is None:
+ color = self.color_cycle[self.plot_index]
+
+ self.plot_index += 1
+ if(self.plot_index >= len(self.color_cycle)):
+ self.plot_index = 0
+
+ self.color_list.append([name, color])
+
+ if marker is None:
+ marker = self.marker_cycle[self.marker_index]
- self.plot_index += 1
- if(self.plot_index >= len(self.color_cycle)):
- self.plot_index = 0
+ self.marker_index += 1
+ if(self.marker_index >= len(self.marker_cycle)):
+ self.marker_index = 0
+
+ self.marker_list.append([sequence, marker])
- self.color_list.append([name, color, marker])
return (color, marker)
def set_linestyle(self, path):
@@ -155,7 +177,6 @@ def plot_confidence_interval(self, ax, x, y, ci, color):
for i in range(len(x)):
top = y[i] + ci[i]
bottom = y[i] - ci[i]
- print(x[i], top, bottom)
ax.plot([x[i], x[i]], [top, bottom], color=color, marker="_", ms=8, solid_capstyle="butt") #, alpha=0.3)
# refreshes the figure according to new changes done
@@ -167,6 +188,14 @@ def change_plot(self, plot_data_collection, user_generated_curves=False):
temporal data
"""
+ try:
+ if(len(plot_data_collection) == 1):
+ self.label_2.setText(plot_data_collection[0].identifiers[0])
+ else:
+ self.label_2.setText("Plot Area")
+ except Exception:
+ self.label_2.setText("Plot Area")
+
# Set the anchor identifier for the first time
# if no identifier has been set so far (similar
# to the selection in BdTableModel update method)
@@ -229,6 +258,28 @@ def change_plot(self, plot_data_collection, user_generated_curves=False):
for plot_data in plot_data_collection:
legend.append(plot_data.identifiers[0])
+ # Get min and max for reference plotting
+ minr = 1e100
+ maxr = -minr
+ miny = 1e100
+ maxy = -miny
+ for plot_data in plot_data_collection:
+ if not plot_data.has_ci:
+ values = ((float(x), float(y)) for (x, y) in plot_data.values)
+ sorted_value_pairs = sorted(values, key=lambda pair: pair[0])
+ [xs, ys] = list(zip(*sorted_value_pairs))
+ else:
+ # A confidence interval is included in the data
+ values = ((float(x), float(y), float(z)) for (x, y, z) in plot_data.values)
+ sorted_value_pairs = sorted(values, key=lambda pair: pair[0])
+ [xs, ys, zs] = list(zip(*sorted_value_pairs))
+ if np.isnan(min(xs)) or np.isnan(max(xs)):
+ continue
+ minr = min(min(xs), minr)
+ maxr = max(max(xs), maxr)
+ miny = min(min(ys), miny)
+ maxy = max(max(ys), maxy)
+
# plot all the lines which are missing yet
plot_count = 0
for plot_data in plot_data_collection:
@@ -236,7 +287,7 @@ def change_plot(self, plot_data_collection, user_generated_curves=False):
l = legend[plot_count] #" ".join([i for i in plot_data.identifiers] + plot_data.path)
if plot_data.color == " ":
- (plot_data.color, plot_data.marker) = self.set_color(plot_data.identifiers[1])
+ (plot_data.color, plot_data.marker) = self.set_color(plot_data.identifiers[1], plot_data.identifiers[0])
plot_data.linestyle = self.set_linestyle(plot_data.path[1])
# Convert list of pairs of strings to two sorted lists of floats
@@ -249,6 +300,10 @@ def change_plot(self, plot_data_collection, user_generated_curves=False):
sorted_value_pairs = sorted(values, key=lambda pair: pair[0])
[xs, ys] = list(zip(*sorted_value_pairs))
+ if len(xs) == 1 and np.isnan(xs[0]):
+ xs = [minr, maxr]
+ ys = [ys[0], ys[0]]
+
# plot the current plot data
curve = self.ax.plot(xs, ys, label=l, color=plot_data.color, marker=plot_data.marker, linestyle=plot_data.linestyle)
@@ -259,6 +314,11 @@ def change_plot(self, plot_data_collection, user_generated_curves=False):
sorted_value_pairs = sorted(values, key=lambda pair: pair[0])
[xs, ys, zs] = list(zip(*sorted_value_pairs))
+ if len(xs) == 1 and np.isnan(xs[0]):
+ xs = [minr, maxr]
+ ys = [ys[0], ys[0]]
+ zs = [zs[0], zs[0]]
+
# calculate the lower and upper boundaries of the CI
ys_low = np.subtract(ys, zs)
ys_up = np.add(ys, zs)
@@ -288,14 +348,32 @@ def change_plot(self, plot_data_collection, user_generated_curves=False):
self.plot_confidence_interval(self.ax, xs, ys, zs, plot_data.color)
plot_count += 1
- except:
- sys.stderr.write("Too many values for confidence interval. Please only add one value.")
+ except Exception as e:
+ print(e)
# Set the legend
if not(legend == ['']):
self.ax.legend(loc='lower right')
DataCursor(self.ax.get_lines())
+ # Specific to MOS plotting
+ try:
+ if plot_data_collection[0].label[1].lower() == "mos":
+ if miny >= 1 and maxy <= 5: # 5-grade scale
+ self.ax.set_ylim(1, 5)
+ elif miny >= 0 and maxy <= 10: # 11-grade scale
+ self.ax.set_ylim(0, 10)
+ elif miny >= 0 and maxy <= 100: # 101-grade scale
+ self.ax.set_ylim(0, 100)
+ elif miny >= -1 and maxy <= 1: # the other one :D
+ self.ax.set_ylim(-1, 1)
+ elif miny >= -2 and maxy <= 2: # the other one :D
+ self.ax.set_ylim(-2, 2)
+ elif miny >= -3 and maxy <= 3: # the other one :D
+ self.ax.set_ylim(-3, 3)
+ except Exception as e:
+ print(e)
+
start, end = self.ax.get_ylim()
data_range = end - start
# get ticks with decimal precision
diff --git a/src/rdplot/__main__.py b/src/rdplot/__main__.py
index 5945c65..cbe2a3d 100644
--- a/src/rdplot/__main__.py
+++ b/src/rdplot/__main__.py
@@ -52,4 +52,5 @@ def main():
sys.exit(app.exec_())
if __name__ == '__main__':
+ print("Starting RD-Plot...")
main()
\ No newline at end of file
diff --git a/src/rdplot/model.py b/src/rdplot/model.py
index b4c6246..0843b67 100644
--- a/src/rdplot/model.py
+++ b/src/rdplot/model.py
@@ -19,7 +19,6 @@
##################################################################################################
from collections import deque
from os.path import sep
-from os import environ
import numpy as np
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QPushButton
@@ -794,7 +793,6 @@ def update(self, sim_data_items, check_add_param = True):
if hasattr(sim_data_item, 'qp') and not QP_added:
self.dialog.chosen_par.addItems(['QP'])
QP_added = True
- # print(sim_data_item.summary_data['encoder_config'])
value_filter = ['.yuv', '.bin', '.hevc', '.jem']
key_filter = []
for sim_class in all_log_configs.keys():
diff --git a/src/rdplot/ui/mainWindow.ui b/src/rdplot/ui/mainWindow.ui
index a27ea48..400f516 100644
--- a/src/rdplot/ui/mainWindow.ui
+++ b/src/rdplot/ui/mainWindow.ui
@@ -263,13 +263,6 @@ padding: 4px;
-
- -
-
-
- Plot CI as area
-
-
-
-
@@ -301,6 +294,13 @@ padding: 4px;
+ -
+
+
+ Plot CI as area
+
+
+
diff --git a/src/rdplot/view.py b/src/rdplot/view.py
index 1161c42..8fbfd64 100644
--- a/src/rdplot/view.py
+++ b/src/rdplot/view.py
@@ -67,9 +67,14 @@ def run(self):
try:
sim_data_items = self._factory.create_item_list_from_path(path)
print("Parsed '{}' ".format(path))
- except SimulationDataItemError:
+ except SimulationDataItemError as error:
self.newParsedData.emit([])
self.path_list.clear()
+
+ # print((
+ # "Could not create simulation data item from file '{}'"
+ # " due to {}"
+ # ).format(path, error))
return
self.newParsedData.emit(sim_data_items)
@@ -117,9 +122,14 @@ def run(self):
try:
sim_data_items = self._factory.create_item_list_from_path(path)
print("Parsed '{}' ".format(path))
- except SimulationDataItemError:
+ except SimulationDataItemError as error:
self.newParsedData.emit([])
self.path_list.clear()
+
+ # print((
+ # "Could not create simulation data item from file '{}'"
+ # " due to {}"
+ # ).format(path, error))
return
self.newParsedData.emit(sim_data_items)