From 66fe6ae828e3ccb53ee2926775f3eff85ed48938 Mon Sep 17 00:00:00 2001 From: Mirco Date: Mon, 12 Jul 2021 21:04:00 +0200 Subject: [PATCH 01/14] Add plotting of confidence intervals for csv data Add try-except-block for CI --- src/rdplot/SimulationDataItem.py | 4 +- .../SimulationDataItemClasses/CsvLogs.py | 11 ++++- src/rdplot/Widgets/MainWindow.py | 12 +++++- src/rdplot/Widgets/PlotWidget.py | 41 +++++++++++++++---- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/rdplot/SimulationDataItem.py b/src/rdplot/SimulationDataItem.py index c9e3a83..b9eb3ec 100644 --- a/src/rdplot/SimulationDataItem.py +++ b/src/rdplot/SimulationDataItem.py @@ -489,11 +489,11 @@ def create_item_list_from_path(self, path): ).format(path)) def parse_csv_item_list(self, log_path): - # we already know that we need the csvlog + # we already know that we need the csv log # currently there is only one...just use it # I think this should not change in the future, # otherwise parts of the file would need to be parsed - # in order to dertmine the type + # in order to determine the type try: from rdplot.SimulationDataItemClasses.CsvLogs import CSVLog diff --git a/src/rdplot/SimulationDataItemClasses/CsvLogs.py b/src/rdplot/SimulationDataItemClasses/CsvLogs.py index 9ec449f..4710c2b 100644 --- a/src/rdplot/SimulationDataItemClasses/CsvLogs.py +++ b/src/rdplot/SimulationDataItemClasses/CsvLogs.py @@ -51,9 +51,18 @@ def __init__(self, config, header, line): data = {} for i in range(0, len(header)): + # skip the header entries if i in [sequence_idx, qp_idx, rate_idx]: continue - data[header[i]] = [(rate, float(line[i]))] + # check if a value has a confidence interval + if line[i].find('+-') == -1: + data[header[i]] = [(rate, float(line[i]))] + continue + else: + ci_value = line[i].split('+-') + data[header[i]] = [(rate, float(ci_value[0]), float(ci_value[1]))] + continue + self.summary_data = data @property diff --git a/src/rdplot/Widgets/MainWindow.py b/src/rdplot/Widgets/MainWindow.py index 83d097d..7d06726 100644 --- a/src/rdplot/Widgets/MainWindow.py +++ b/src/rdplot/Widgets/MainWindow.py @@ -476,7 +476,11 @@ def change_table_temporal(self, plot_data_collect): header = legend[0] for plot_data in plot_data_collection: - values = ((float(x), float(y)) for (x, y) in plot_data.values) + # check if confidence interval is used + if len(plot_data.values[0]) == 2: + values = ((float(x), float(y)) for (x, y) in plot_data.values) + else: + values = ((float(x), float(y)) for (x, y, z) in plot_data.values) sorted_value_pairs = sorted(values, key=lambda pair: pair[0]) [xs, ys] = list(zip(*sorted_value_pairs)) @@ -533,7 +537,11 @@ def change_table_summary(self, plot_data_collect): for plot_data in plot_data_collection: - values = ((float(x), float(y)) for (x, y) in plot_data.values) + # check if confidence interval is used + if len(plot_data.values[0]) == 2: + values = ((float(x), float(y)) for (x, y) in plot_data.values) + else: + values = ((float(x), float(y)) for (x, y, z) in plot_data.values) sorted_value_pairs = sorted(values, key=lambda pair: pair[0]) [xs, ys] = list(zip(*sorted_value_pairs)) diff --git a/src/rdplot/Widgets/PlotWidget.py b/src/rdplot/Widgets/PlotWidget.py index e72f1b8..c8e0b98 100644 --- a/src/rdplot/Widgets/PlotWidget.py +++ b/src/rdplot/Widgets/PlotWidget.py @@ -23,6 +23,8 @@ import matplotlib matplotlib.use('Qt5Agg') +import sys + from matplotlib.figure import Figure from matplotlib import cbook from scipy import spatial @@ -161,15 +163,36 @@ 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) # Convert list of pairs of strings to two sorted lists of floats - 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)) - - # plot the current plotdata and set the legend - curve = self.ax.plot(xs, ys, label=l) - - plot_count += 1 - + # Check if a confidence interval is given + try: + if len(plot_data.values[0]) == 2: + 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)) + + # plot the current plot data + curve = self.ax.plot(xs, ys, label=l) + + plot_count += 1 + else: + 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)) + + # plot the current plot data + ys_low = np.subtract(ys, zs) + ys_up = np.add(ys, zs) + ys_ci = np.concatenate((ys_low, ys_up[::-1])) + xs_ci = np.concatenate((xs, xs[::-1])) + + curve = self.ax.plot(xs, ys, label=l) + curve_ci = self.ax.fill(xs_ci, ys_ci, alpha=0.3, ec='black') + + plot_count += 1 + except: + sys.stderr.write("Too many values for confidence interval. Please only add one value.") + + # Set the legend if not(legend == ['']): self.ax.legend(loc='lower right') DataCursor(self.ax.get_lines()) From ebfbb389b4d1eb87ebf225e32c5578a52f710458 Mon Sep 17 00:00:00 2001 From: Mirco Date: Tue, 13 Jul 2021 10:09:56 +0200 Subject: [PATCH 02/14] Add ci-csv files to the csv-test --- .../exampleCsv/{ => CSV}/HDR-HLG_HM-16.22.csv | 0 .../exampleCsv/{ => CSV}/HDR-HLG_VTM-11.0.csv | 0 .../test_logs/exampleCsv/CSV_CI/test_ci1.csv | 25 +++++++++++++++++++ .../test_logs/exampleCsv/CSV_CI/test_ci2.csv | 25 +++++++++++++++++++ 4 files changed, 50 insertions(+) rename src/rdplot/tests/test_logs/exampleCsv/{ => CSV}/HDR-HLG_HM-16.22.csv (100%) rename src/rdplot/tests/test_logs/exampleCsv/{ => CSV}/HDR-HLG_VTM-11.0.csv (100%) create mode 100644 src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci1.csv create mode 100644 src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci2.csv diff --git a/src/rdplot/tests/test_logs/exampleCsv/HDR-HLG_HM-16.22.csv b/src/rdplot/tests/test_logs/exampleCsv/CSV/HDR-HLG_HM-16.22.csv similarity index 100% rename from src/rdplot/tests/test_logs/exampleCsv/HDR-HLG_HM-16.22.csv rename to src/rdplot/tests/test_logs/exampleCsv/CSV/HDR-HLG_HM-16.22.csv diff --git a/src/rdplot/tests/test_logs/exampleCsv/HDR-HLG_VTM-11.0.csv b/src/rdplot/tests/test_logs/exampleCsv/CSV/HDR-HLG_VTM-11.0.csv similarity index 100% rename from src/rdplot/tests/test_logs/exampleCsv/HDR-HLG_VTM-11.0.csv rename to src/rdplot/tests/test_logs/exampleCsv/CSV/HDR-HLG_VTM-11.0.csv diff --git a/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci1.csv b/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci1.csv new file mode 100644 index 0000000..378fe27 --- /dev/null +++ b/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci1.csv @@ -0,0 +1,25 @@ +Sequence;QP;Bitrate;MOS +Sequence01;27;15544.26;7.65+-0.31 +Sequence01;32;8207.94;5.12+-0.44 +Sequence01;37;4677.54;3.53+-0.24 +Sequence01;42;2696.16;2.08+-0.32 +Sequence02;30;11802.85;6.92+-0.27 +Sequence02;34;7243.2;5.03+-0.37 +Sequence02;38;4695.95;3.12+-0.43 +Sequence02;42;3090.75;2.02+-0.33 +Sequence03;28;17165.46;6.75+-0.27 +Sequence03;32;9502.2;4.82+-0.25 +Sequence03;36;5767.26;3.72+-0.25 +Sequence03;42;3628.44;2.54+-0.28 +Sequence04;30;7664.7;6.82+-0.22 +Sequence04;34;4649.76;4.33+-0.3 +Sequence04;38;3449.92;2.44+-0.39 +Sequence04;42;2492.52;0.55+-0.41 +Sequence05;26;2705.64;7.02+-0.48 +Sequence05;30;1723.74;5.72+-0.38 +Sequence05;34;1139.64;4.73+-0.52 +Sequence05;40;636.96;3.12+-0.38 +Sequence06;28;3651.72;7.2+-0.46 +Sequence06;32;2014.02;5.64+-0.61 +Sequence06;36;1166.88;4.21+-0.45 +Sequence06;40;678.06;2.53+-0.55 diff --git a/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci2.csv b/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci2.csv new file mode 100644 index 0000000..2f9d26f --- /dev/null +++ b/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci2.csv @@ -0,0 +1,25 @@ +Sequence;QP;Bitrate;MOS +Sequence01;23;10620.9;7.35+-0.24 +Sequence01;28;5359.38;5.77+-0.24 +Sequence01;33;2883.36;3.82+-0.27 +Sequence01;37;1834.08;3.01+-0.27 +Sequence02;26;7284.9;7.48+-0.3 +Sequence02;29;4694.9;6.12+-0.46 +Sequence02;33;2732.7;4.23+-0.34 +Sequence02;37;1722.8;2.95+-0.39 +Sequence03;24;12248.78;6.73+-0.42 +Sequence03;28;6779.34;5.04+-0.2 +Sequence03;31;4517.04;3.68+-0.24 +Sequence03;35;2738.34;3.07+-0.35 +Sequence04;24;4522.26;7.23+-0.26 +Sequence04;28;3005.34;6.04+-0.27 +Sequence04;31;2296.86;5.12+-0.34 +Sequence04;34;1788.42;4.24+-0.29 +Sequence05;24;1724.46;7.48+-0.3 +Sequence05;26;1306.98;6.42+-0.56 +Sequence05;30;796.2;4.41+-0.62 +Sequence05;32;618.9;3.4+-0.51 +Sequence06;25;2410.56;7.43+-0.33 +Sequence06;27;1677.3;6.39+-0.35 +Sequence06;30;1034.4;4.41+-0.3 +Sequence06;33;647.1;3.02+-0.57 From 6a3a707ebaebbcc0fec5486f7f08eaa1f4de62bf Mon Sep 17 00:00:00 2001 From: Mirco Date: Sat, 24 Jul 2021 15:23:10 +0200 Subject: [PATCH 03/14] Modify ci definition and parsing CI values are now defined as columns with name '-CI', csv-test files adjusted for this change, added commentary for CI integration --- .gitignore | 3 ++ .../SimulationDataItemClasses/CsvLogs.py | 30 ++++++++--- src/rdplot/Widgets/MainWindow.py | 8 ++- src/rdplot/Widgets/PlotWidget.py | 8 ++- .../test_logs/exampleCsv/CSV_CI/test_ci1.csv | 50 +++++++++---------- .../test_logs/exampleCsv/CSV_CI/test_ci2.csv | 50 +++++++++---------- 6 files changed, 89 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index 43d0d60..39e4ac8 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,6 @@ example_simulation_data #additional data logs datDec/ + +# macos +.DS_STORE \ No newline at end of file diff --git a/src/rdplot/SimulationDataItemClasses/CsvLogs.py b/src/rdplot/SimulationDataItemClasses/CsvLogs.py index 4710c2b..cc909d3 100644 --- a/src/rdplot/SimulationDataItemClasses/CsvLogs.py +++ b/src/rdplot/SimulationDataItemClasses/CsvLogs.py @@ -54,14 +54,32 @@ def __init__(self, config, header, line): # skip the header entries if i in [sequence_idx, qp_idx, rate_idx]: continue - # check if a value has a confidence interval - if line[i].find('+-') == -1: - data[header[i]] = [(rate, float(line[i]))] + # check if value is a confidence value (CI) + # if entry is a ci-value we can skip it, since + # it will be later processed with the according value + if header[i].find('-ci') != -1: continue else: - ci_value = line[i].split('+-') - data[header[i]] = [(rate, float(ci_value[0]), float(ci_value[1]))] - continue + # Check if CI value can be found, else just read the data. + # In case that a CI value is found, store its header index. + # Afterwards store the value and CI into a tuple with three + # entries (rate, value, ci-value). Otherwise we will just + # store the data in a tuple with two entries (rate, value). + # CI columns are always labeled as '-CI'. + ci_idx = -1 + for j in range(0, len(header)): + if header[j].find(header[i]+'-ci') != -1: + ci_idx = j + break + + if ci_idx == -1: + # Read only the data (no CI available) + data[header[i]] = [(rate, float(line[i]))] + continue + else: + # Read the data and CI in one tuple + data[header[i]] = [(rate, float(line[i]), float(line[ci_idx]))] + continue self.summary_data = data diff --git a/src/rdplot/Widgets/MainWindow.py b/src/rdplot/Widgets/MainWindow.py index 7d06726..452dfd7 100644 --- a/src/rdplot/Widgets/MainWindow.py +++ b/src/rdplot/Widgets/MainWindow.py @@ -476,7 +476,9 @@ def change_table_temporal(self, plot_data_collect): header = legend[0] for plot_data in plot_data_collection: - # check if confidence interval is used + # check if plot_data value has a confidence interval + # confidence intervals are stored in tuples with three entries + # (rate, value, ci-value) instead of (rate, value) in the default case if len(plot_data.values[0]) == 2: values = ((float(x), float(y)) for (x, y) in plot_data.values) else: @@ -537,7 +539,9 @@ def change_table_summary(self, plot_data_collect): for plot_data in plot_data_collection: - # check if confidence interval is used + # check if plot_data value has a confidence interval + # confidence intervals are stored in tuples with three entries + # (rate, value, ci-value) instead of (rate, value) in the default case if len(plot_data.values[0]) == 2: values = ((float(x), float(y)) for (x, y) in plot_data.values) else: diff --git a/src/rdplot/Widgets/PlotWidget.py b/src/rdplot/Widgets/PlotWidget.py index c8e0b98..43d8230 100644 --- a/src/rdplot/Widgets/PlotWidget.py +++ b/src/rdplot/Widgets/PlotWidget.py @@ -163,7 +163,9 @@ 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) # Convert list of pairs of strings to two sorted lists of floats - # Check if a confidence interval is given + # Check if plot_data value has a confidence interval + # Confidence intervals are stored in tuples with three entries + # (rate, value, ci-value) instead of (rate, value) in the default case try: if len(plot_data.values[0]) == 2: values = ((float(x), float(y)) for (x, y) in plot_data.values) @@ -175,16 +177,18 @@ def change_plot(self, plot_data_collection, user_generated_curves=False): plot_count += 1 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)) - # plot the current plot data + # calculate the lower and upper boundaries of the CI ys_low = np.subtract(ys, zs) ys_up = np.add(ys, zs) ys_ci = np.concatenate((ys_low, ys_up[::-1])) xs_ci = np.concatenate((xs, xs[::-1])) + # plot the curve and afterwards the CI as polygon curve = self.ax.plot(xs, ys, label=l) curve_ci = self.ax.fill(xs_ci, ys_ci, alpha=0.3, ec='black') diff --git a/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci1.csv b/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci1.csv index 378fe27..067ac25 100644 --- a/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci1.csv +++ b/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci1.csv @@ -1,25 +1,25 @@ -Sequence;QP;Bitrate;MOS -Sequence01;27;15544.26;7.65+-0.31 -Sequence01;32;8207.94;5.12+-0.44 -Sequence01;37;4677.54;3.53+-0.24 -Sequence01;42;2696.16;2.08+-0.32 -Sequence02;30;11802.85;6.92+-0.27 -Sequence02;34;7243.2;5.03+-0.37 -Sequence02;38;4695.95;3.12+-0.43 -Sequence02;42;3090.75;2.02+-0.33 -Sequence03;28;17165.46;6.75+-0.27 -Sequence03;32;9502.2;4.82+-0.25 -Sequence03;36;5767.26;3.72+-0.25 -Sequence03;42;3628.44;2.54+-0.28 -Sequence04;30;7664.7;6.82+-0.22 -Sequence04;34;4649.76;4.33+-0.3 -Sequence04;38;3449.92;2.44+-0.39 -Sequence04;42;2492.52;0.55+-0.41 -Sequence05;26;2705.64;7.02+-0.48 -Sequence05;30;1723.74;5.72+-0.38 -Sequence05;34;1139.64;4.73+-0.52 -Sequence05;40;636.96;3.12+-0.38 -Sequence06;28;3651.72;7.2+-0.46 -Sequence06;32;2014.02;5.64+-0.61 -Sequence06;36;1166.88;4.21+-0.45 -Sequence06;40;678.06;2.53+-0.55 +Sequence;QP;Bitrate;MOS;MOS-CI +Sequence01;27;15544.26;7.65;0.31 +Sequence01;32;8207.94;5.12;0.44 +Sequence01;37;4677.54;3.53;0.24 +Sequence01;42;2696.16;2.08;0.32 +Sequence02;30;11802.85;6.92;0.27 +Sequence02;34;7243.2;5.03;0.37 +Sequence02;38;4695.95;3.12;0.43 +Sequence02;42;3090.75;2.02;0.33 +Sequence03;28;17165.46;6.75;0.27 +Sequence03;32;9502.2;4.82;0.25 +Sequence03;36;5767.26;3.72;0.25 +Sequence03;42;3628.44;2.54;0.28 +Sequence04;30;7664.7;6.82;0.22 +Sequence04;34;4649.76;4.33;0.3 +Sequence04;38;3449.92;2.44;0.39 +Sequence04;42;2492.52;0.55;0.41 +Sequence05;26;2705.64;7.02;0.48 +Sequence05;30;1723.74;5.72;0.38 +Sequence05;34;1139.64;4.73;0.52 +Sequence05;40;636.96;3.12;0.38 +Sequence06;28;3651.72;7.2;0.46 +Sequence06;32;2014.02;5.64;0.61 +Sequence06;36;1166.88;4.21;0.45 +Sequence06;40;678.06;2.53;0.55 diff --git a/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci2.csv b/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci2.csv index 2f9d26f..f24eec0 100644 --- a/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci2.csv +++ b/src/rdplot/tests/test_logs/exampleCsv/CSV_CI/test_ci2.csv @@ -1,25 +1,25 @@ -Sequence;QP;Bitrate;MOS -Sequence01;23;10620.9;7.35+-0.24 -Sequence01;28;5359.38;5.77+-0.24 -Sequence01;33;2883.36;3.82+-0.27 -Sequence01;37;1834.08;3.01+-0.27 -Sequence02;26;7284.9;7.48+-0.3 -Sequence02;29;4694.9;6.12+-0.46 -Sequence02;33;2732.7;4.23+-0.34 -Sequence02;37;1722.8;2.95+-0.39 -Sequence03;24;12248.78;6.73+-0.42 -Sequence03;28;6779.34;5.04+-0.2 -Sequence03;31;4517.04;3.68+-0.24 -Sequence03;35;2738.34;3.07+-0.35 -Sequence04;24;4522.26;7.23+-0.26 -Sequence04;28;3005.34;6.04+-0.27 -Sequence04;31;2296.86;5.12+-0.34 -Sequence04;34;1788.42;4.24+-0.29 -Sequence05;24;1724.46;7.48+-0.3 -Sequence05;26;1306.98;6.42+-0.56 -Sequence05;30;796.2;4.41+-0.62 -Sequence05;32;618.9;3.4+-0.51 -Sequence06;25;2410.56;7.43+-0.33 -Sequence06;27;1677.3;6.39+-0.35 -Sequence06;30;1034.4;4.41+-0.3 -Sequence06;33;647.1;3.02+-0.57 +Sequence;QP;Bitrate;MOS;MOS-CI +Sequence01;23;10620.9;7.35;0.24 +Sequence01;28;5359.38;5.77;0.24 +Sequence01;33;2883.36;3.82;0.27 +Sequence01;37;1834.08;3.01;0.27 +Sequence02;26;7284.9;7.48;0.3 +Sequence02;29;4694.9;6.12;0.46 +Sequence02;33;2732.7;4.23;0.34 +Sequence02;37;1722.8;2.95;0.39 +Sequence03;24;12248.78;6.73;0.42 +Sequence03;28;6779.34;5.04;0.2 +Sequence03;31;4517.04;3.68;0.24 +Sequence03;35;2738.34;3.07;0.35 +Sequence04;24;4522.26;7.23;0.26 +Sequence04;28;3005.34;6.04;0.27 +Sequence04;31;2296.86;5.12;0.34 +Sequence04;34;1788.42;4.24;0.29 +Sequence05;24;1724.46;7.48;0.3 +Sequence05;26;1306.98;6.42;0.56 +Sequence05;30;796.2;4.41;0.62 +Sequence05;32;618.9;3.4;0.51 +Sequence06;25;2410.56;7.43;0.33 +Sequence06;27;1677.3;6.39;0.35 +Sequence06;30;1034.4;4.41;0.3 +Sequence06;33;647.1;3.02;0.57 From 236158b6e4b6d581d0bdf21194318297f5a1db9a Mon Sep 17 00:00:00 2001 From: Mirco Date: Sun, 8 Aug 2021 16:32:14 +0200 Subject: [PATCH 04/14] Add bjontegaard ci table updates and plotting Bjontegaard calculations are now adjusted to the mode of ci (average, worst, best), which can be adjusted in a new combo box. The plot will show the selected curves (borders) in the confidence intervals. The anchor can be selected by double clicking the table headers. Additionally, mixed plotting of ci and non-ci curves are now possible. --- src/rdplot/SimulationDataItem.py | 6 ++++ src/rdplot/Widgets/MainWindow.py | 62 ++++++++++++++++++++++++++++++-- src/rdplot/Widgets/PlotWidget.py | 36 ++++++++++++++++--- src/rdplot/lib/BD.py | 36 +++++++++++++++---- src/rdplot/model.py | 32 +++++++++++++---- src/rdplot/ui/mainWindow.ui | 16 +++++++-- 6 files changed, 165 insertions(+), 23 deletions(-) diff --git a/src/rdplot/SimulationDataItem.py b/src/rdplot/SimulationDataItem.py index b9eb3ec..2957864 100644 --- a/src/rdplot/SimulationDataItem.py +++ b/src/rdplot/SimulationDataItem.py @@ -167,6 +167,12 @@ def __init__(self, identifiers=[], values=[], path=[], label=()): self.values = values self.path = path self.label = label + self.ci = False + + # plot data has confidence interval if tuple has 3 entries + # tuple: (rate, value, ci-value) + if len(self.values[0]) == 3: + self.ci = True class SimulationDataItemError(Exception): diff --git a/src/rdplot/Widgets/MainWindow.py b/src/rdplot/Widgets/MainWindow.py index 452dfd7..0d7df17 100644 --- a/src/rdplot/Widgets/MainWindow.py +++ b/src/rdplot/Widgets/MainWindow.py @@ -63,7 +63,7 @@ def __init__(self, ): self.plotPreview.tableView.setModel(self.bdTableModel) # connect a double clicked section of the bd table to a change of the anchor - self.plotPreview.tableView.horizontalHeader().sectionDoubleClicked.connect(self.update_bd_table) + self.plotPreview.tableView.horizontalHeader().sectionDoubleClicked.connect(self.update_doubleClicked_horizontalHeader) self.plotPreview.tableView.verticalHeader().sectionDoubleClicked.connect(self.update_bd_user_generated_curves_table) # Set custom selection model, so that sub items are automatically @@ -138,8 +138,11 @@ def __init__(self, ): # set up combo boxes for rate/psnr and interpolation options self.combo_interp.addItems(["pchip", "pol"]) self.combo_rate_psnr.addItems(["drate", "dsnr"]) + self.combo_ci.addItems(["average", "worst", "best"]) self.combo_interp.currentIndexChanged.connect(self.on_combo_box) self.combo_rate_psnr.currentIndexChanged.connect(self.on_combo_box) + self.combo_ci.currentIndexChanged.connect(self.on_ci_combo_box) + self.combo_ci.setEnabled(False) # set up bd plot checkbox self.checkBox_bdplot.stateChanged.connect(self.update_bd_plot) @@ -272,6 +275,9 @@ def change_list(self, q_selected, q_deselected): # overwriting keys is. self.selectedSimulationDataItemListModel.clear_and_update_from_tuples(tuples) + # reset the ci combo box if list selection changed + self.combo_ci.setCurrentIndex(0) + def get_selected_simulation_data_items(self): return [self.selectedSimulationDataItemListModel[key] for key in self.selectedSimulationDataItemListModel] @@ -404,6 +410,16 @@ def update_plot(self): return plot_data_collection = data_collection + data_collection_user_generated + + # check if ci values are contained in the plot data + # enable the ci combo box if at least one data point + # has a confidence interval + self.combo_ci.setEnabled(False) + for plot_data in plot_data_collection: + if plot_data.ci: + self.combo_ci.setEnabled(True) + break + if len(data_collection_user_generated): self.plotPreview.tableView.setModel(self.bdUserGeneratedTableModel) self.plotPreview.change_plot(plot_data_collection, True) @@ -598,6 +614,27 @@ def change_table_summary(self, plot_data_collect): column_saver = config_count = 0 data_count += 1 + def update_doubleClicked_horizontalHeader(self, index): + # update the bd table (new anchor) + self.update_bd_table(index) + + # update the anchor identifier and ci mode for the ci plot view + self.plotPreview.anchor_identifier = self.bdTableModel.getAnchorIdentifier() + self.plotPreview.ci_mode = self.combo_ci.currentText() + + # update plot + self.check_labels() + data_collection = self.get_plot_data_collection_from_selected_variables() + data_collection_user_generated = [] + for index in self.curveListView.selectedIndexes(): + data_collection_user_generated.append(self.curveListModel[index.data()]) + + plot_data_collection = data_collection + data_collection_user_generated + + self.plotPreview.tableView.setModel(self.bdTableModel) + self.update_table(data_collection) + self.plotPreview.change_plot(plot_data_collection, False) + def update_bd_table(self, index): # update bd table, the index determines the anchor, # if it is non integer per default the first config is regarded as @@ -605,7 +642,8 @@ def update_bd_table(self, index): self.bdTableModel.update_table(self.combo_rate_psnr.currentText(), self.combo_interp.currentText(), index, - not(self.checkBox_bdplot.isChecked())) + not(self.checkBox_bdplot.isChecked()), + self.combo_ci.currentText()) def update_bd_user_generated_curves_table(self, index): clicked_text = self.bdUserGeneratedTableModel.headerData(index, Qt.Vertical, Qt.DisplayRole) @@ -659,6 +697,26 @@ def on_combo_box(self): # just update the bd table but do not change the anchor self.update_bd_table(-1) + def on_ci_combo_box(self): + # update the bd table (new ci mode) + self.update_bd_table(-1) + + # update the ci mode for the ci plot view + self.plotPreview.ci_mode = self.combo_ci.currentText() + + # update plot + self.check_labels() + data_collection = self.get_plot_data_collection_from_selected_variables() + data_collection_user_generated = [] + for index in self.curveListView.selectedIndexes(): + data_collection_user_generated.append(self.curveListModel[index.data()]) + + plot_data_collection = data_collection + data_collection_user_generated + + self.plotPreview.tableView.setModel(self.bdTableModel) + self.update_table(data_collection) + self.plotPreview.change_plot(plot_data_collection, False) + def save_current_selection(self): """Saves the current selected sim data item collection""" if not self.get_selected_simulation_data_items(): diff --git a/src/rdplot/Widgets/PlotWidget.py b/src/rdplot/Widgets/PlotWidget.py index 43d8230..ac1ff2d 100644 --- a/src/rdplot/Widgets/PlotWidget.py +++ b/src/rdplot/Widgets/PlotWidget.py @@ -82,6 +82,10 @@ def __init__(self, ): self.label_warning.hide() + # Anchor identifier for ci plots + self.anchor_identifier = '' + self.ci_mode = 'average' + def create_legend(self, plot_data_collection): tmp_legend = [] for plot_data in plot_data_collection: @@ -109,6 +113,17 @@ def change_plot(self, plot_data_collection, user_generated_curves=False): temporal data """ + # Set the anchor identifier for the first time + # if no identifier has been set so far (similar + # to the selection in BdTableModel update method) + if self.anchor_identifier == '': + config_set = set() + for i in plot_data_collection: + config_set.add('+'.join(i.identifiers[1:])) + config_set = sorted(config_set) + config = list(config_set) + self.anchor_identifier = config[0] + if len(plot_data_collection) == 0: self._clear_plot() return @@ -167,7 +182,7 @@ def change_plot(self, plot_data_collection, user_generated_curves=False): # Confidence intervals are stored in tuples with three entries # (rate, value, ci-value) instead of (rate, value) in the default case try: - if len(plot_data.values[0]) == 2: + if not plot_data.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)) @@ -188,9 +203,22 @@ def change_plot(self, plot_data_collection, user_generated_curves=False): ys_ci = np.concatenate((ys_low, ys_up[::-1])) xs_ci = np.concatenate((xs, xs[::-1])) - # plot the curve and afterwards the CI as polygon - curve = self.ax.plot(xs, ys, label=l) - curve_ci = self.ax.fill(xs_ci, ys_ci, alpha=0.3, ec='black') + # plot the curve (depending on ci mode) + if self.ci_mode == 'average': + curve = self.ax.plot(xs, ys, label=l) + elif self.ci_mode == 'best': + if plot_data.identifiers[1] == self.anchor_identifier: + curve = self.ax.plot(xs, ys_low, label=l) + else: + curve = self.ax.plot(xs, ys_up, label=l) + elif self.ci_mode == 'worst': + if plot_data.identifiers[1] == self.anchor_identifier: + curve = self.ax.plot(xs, ys_up, label=l) + else: + curve = self.ax.plot(xs, ys_low, label=l) + + # plot the ci as polygon around the current curve + poly_ci = self.ax.fill(xs_ci, ys_ci, c=curve[0].get_c(), ec=curve[0].get_c(), alpha=0.3) plot_count += 1 except: diff --git a/src/rdplot/lib/BD.py b/src/rdplot/lib/BD.py index c9e98cd..58aa078 100644 --- a/src/rdplot/lib/BD.py +++ b/src/rdplot/lib/BD.py @@ -245,7 +245,8 @@ def find_diff(poly1, poly2, max_int, min_int): return avg_diff -def bjontegaard(curve1, curve2, mode='dsnr', interpol='pol', seq='', d=list(), testmode=False): +def bjontegaard(curve1, curve2, mode='dsnr', interpol='pol', seq='', d=list(), testmode=False, ci1_mode='average', + ci2_mode='average'): """ Bjontegaard metric calculation Bjontegaard's metric allows to compute the average gain in PSNR or the @@ -279,12 +280,33 @@ def bjontegaard(curve1, curve2, mode='dsnr', interpol='pol', seq='', d=list(), t curve1 = sorted(curve1, key=lambda tup: tup[1]) curve2 = sorted(curve2, key=lambda tup: tup[1]) - # convert rates in logarithmic units - psnr1 = [i[1] for i in curve1] - rate1 = [math.log(i[0]) for i in curve1] - - psnr2 = [i[1] for i in curve2] - rate2 = [math.log(i[0]) for i in curve2] + # select the first curve according to ci mode + if ci1_mode == 'best': + # convert rates in logarithmic units + psnr1 = [i[1]-i[2] for i in curve1] + rate1 = [math.log(i[0]) for i in curve1] + elif ci1_mode == 'worst': + # convert rates in logarithmic units + psnr1 = [i[1]+i[2] for i in curve1] + rate1 = [math.log(i[0]) for i in curve1] + else: + # convert rates in logarithmic units + psnr1 = [i[1] for i in curve1] + rate1 = [math.log(i[0]) for i in curve1] + + # select the second curve according to ci mode + if ci2_mode == 'best': + # convert rates in logarithmic units + psnr2 = [i[1]+i[2] for i in curve2] + rate2 = [math.log(i[0]) for i in curve2] + elif ci2_mode == 'worst': + # convert rates in logarithmic units + psnr2 = [i[1]-i[2] for i in curve2] + rate2 = [math.log(i[0]) for i in curve2] + else: + # convert rates in logarithmic units + psnr2 = [i[1] for i in curve2] + rate2 = [math.log(i[0]) for i in curve2] if mode == 'dsnr': return bdsnr(rate1, psnr1, rate2, psnr2, interpol, seq, d, testmode) diff --git a/src/rdplot/model.py b/src/rdplot/model.py index 62d949b..4f9602f 100644 --- a/src/rdplot/model.py +++ b/src/rdplot/model.py @@ -928,6 +928,9 @@ def __init__(self, parent=None, *args): self._anchor_index = 0 self._plot_data_collection = [] + def getAnchorIdentifier(self): + return self._horizontal_headers[self._anchor_index] + def rowCount(self, parent): return self._data.shape[0] @@ -1049,7 +1052,7 @@ def update(self, plot_data_collection, bd_option, interp_option, bd_plot): # This function is called when the anchor, the interpolation method # or the output of the bjontegaard delta is changed - def update_table(self, bd_option, interp_option, anchor_index, bd_plot): + def update_table(self, bd_option, interp_option, anchor_index, bd_plot, ci_mode='average'): # if there are no rows and columns in the model, # nothing can be updated if bd_plot: @@ -1111,8 +1114,15 @@ def update_table(self, bd_option, interp_option, anchor_index, bd_plot): # get the rd values for curve c1 which is the anchor c1 = [x for x in self._plot_data_collection if - '+'.join(x.identifiers).__eq__('+'.join([identifiers_tmp[0], anchor]))][0].values - c1 = sorted(list(set(c1))) # remove duplicates, this is just a workaround for the moment.... + '+'.join(x.identifiers).__eq__('+'.join([identifiers_tmp[0], anchor]))] + + # set the ci mode values for c1 + ci1_mode = 'average' # no ci value available + if c1[0].ci: + ci1_mode = ci_mode + + # sort the rd values for curve c1 + curve1 = sorted(list(set(c1[0].values))) # remove duplicates, this is just a workaround for the moment # if the configuration is not available for the current seq continue if len([x for x in self._plot_data_collection if @@ -1123,19 +1133,27 @@ def update_table(self, bd_option, interp_option, anchor_index, bd_plot): # get the rd values for curve c2 c2 = [x for x in self._plot_data_collection - if '+'.join(x.identifiers).__eq__('+'.join(identifiers_tmp))][0].values - c2 = sorted(list(set(c2))) + if '+'.join(x.identifiers).__eq__('+'.join(identifiers_tmp))] + + # set the ci mode values for c2 + ci2_mode = 'average' # no ci value available + if c2[0].ci: + ci2_mode = ci_mode + + # sort the rd values for curve c2 + curve2 = sorted(list(set(c2[0].values))) # remove duplicates, this is just a workaround for the moment # if a simulation does not contain at least 4 rate points, # we do not want to calculate the bd - if len(c1) < 4 or len(c2) < 4: + if len(curve1) < 4 or len(curve2) < 4: self._data[row, col] = np.nan col += 1 continue # calculate the bd, actually this can be extended by some plots configs = [anchor, identifiers_tmp[1]] - self._data[row, col] = bjontegaard(c1, c2, bd_option, interp_option, 'BD Plot ' + seq, configs, bd_plot) + self._data[row, col] = bjontegaard(curve1, curve2, bd_option, interp_option, 'BD Plot ' + seq, configs, + bd_plot, ci1_mode, ci2_mode) col += 1 row += 1 diff --git a/src/rdplot/ui/mainWindow.ui b/src/rdplot/ui/mainWindow.ui index 6cb34e7..8c96cca 100644 --- a/src/rdplot/ui/mainWindow.ui +++ b/src/rdplot/ui/mainWindow.ui @@ -7,7 +7,7 @@ 0 0 1253 - 1000 + 784 @@ -209,7 +209,7 @@ padding: 4px; 200 - 232 + 251 @@ -249,6 +249,16 @@ padding: 4px; + + + + true + + + + + + @@ -265,7 +275,7 @@ padding: 4px; 0 0 1253 - 23 + 24 From 2c8d430fc417d50fe48d52901b5c8e849582beb4 Mon Sep 17 00:00:00 2001 From: Mirco Dilly Date: Mon, 23 Aug 2021 11:27:43 +0200 Subject: [PATCH 05/14] Bug fixes and unit labels for csv files --- src/rdplot/SimulationDataItem.py | 4 ++-- src/rdplot/SimulationDataItemClasses/CsvLogs.py | 5 ++++- src/rdplot/Widgets/MainWindow.py | 14 ++++++++++---- src/rdplot/Widgets/PlotWidget.py | 7 +++++-- src/rdplot/model.py | 11 +++++++---- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/rdplot/SimulationDataItem.py b/src/rdplot/SimulationDataItem.py index 2957864..e580799 100644 --- a/src/rdplot/SimulationDataItem.py +++ b/src/rdplot/SimulationDataItem.py @@ -167,12 +167,12 @@ def __init__(self, identifiers=[], values=[], path=[], label=()): self.values = values self.path = path self.label = label - self.ci = False + self.has_ci = False # plot data has confidence interval if tuple has 3 entries # tuple: (rate, value, ci-value) if len(self.values[0]) == 3: - self.ci = True + self.has_ci = True class SimulationDataItemError(Exception): diff --git a/src/rdplot/SimulationDataItemClasses/CsvLogs.py b/src/rdplot/SimulationDataItemClasses/CsvLogs.py index cc909d3..cb84d91 100644 --- a/src/rdplot/SimulationDataItemClasses/CsvLogs.py +++ b/src/rdplot/SimulationDataItemClasses/CsvLogs.py @@ -103,7 +103,10 @@ def _get_label(self, keys): :param keys: Variable/Path for which to get the labels :return: tuple of labels: (x-axis label, y-axis label) """ - label = ('kbps', 'dB') + if keys[1].find('psnr') != -1: + label = ('kbps','dB') + else: + label = ('kbps', keys[1]) return label diff --git a/src/rdplot/Widgets/MainWindow.py b/src/rdplot/Widgets/MainWindow.py index 0d7df17..16842ef 100644 --- a/src/rdplot/Widgets/MainWindow.py +++ b/src/rdplot/Widgets/MainWindow.py @@ -406,17 +406,23 @@ def update_plot(self): data_collection_user_generated = [] for index in self.curveListView.selectedIndexes(): data_collection_user_generated.append(self.curveListModel[index.data()]) + + # Update the anchor identifier for the plot preview which is selected by the + # user in the bdTableModel by clicking on the header lines + self.plotPreview.anchor_identifier = self.bdTableModel.getAnchorIdentifier() else: return plot_data_collection = data_collection + data_collection_user_generated - # check if ci values are contained in the plot data - # enable the ci combo box if at least one data point - # has a confidence interval + # Check if ci values are contained in the plot data. Enable the ci combo box, if at + # least one data point has a confidence interval and set the current plot anchor with + # respect to the anchor of the bdTableModel. Combo_ci is only available if more than + # one plot is selected by the user. self.combo_ci.setEnabled(False) + for plot_data in plot_data_collection: - if plot_data.ci: + if plot_data.has_ci and len(plot_data_collection) > 1: self.combo_ci.setEnabled(True) break diff --git a/src/rdplot/Widgets/PlotWidget.py b/src/rdplot/Widgets/PlotWidget.py index ac1ff2d..54df7a3 100644 --- a/src/rdplot/Widgets/PlotWidget.py +++ b/src/rdplot/Widgets/PlotWidget.py @@ -122,7 +122,10 @@ def change_plot(self, plot_data_collection, user_generated_curves=False): config_set.add('+'.join(i.identifiers[1:])) config_set = sorted(config_set) config = list(config_set) - self.anchor_identifier = config[0] + try: + self.anchor_identifier = config[0] + except: + self.anchor_identifier = '' if len(plot_data_collection) == 0: self._clear_plot() @@ -182,7 +185,7 @@ def change_plot(self, plot_data_collection, user_generated_curves=False): # Confidence intervals are stored in tuples with three entries # (rate, value, ci-value) instead of (rate, value) in the default case try: - if not plot_data.ci: + 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)) diff --git a/src/rdplot/model.py b/src/rdplot/model.py index 4f9602f..2510449 100644 --- a/src/rdplot/model.py +++ b/src/rdplot/model.py @@ -929,7 +929,10 @@ def __init__(self, parent=None, *args): self._plot_data_collection = [] def getAnchorIdentifier(self): - return self._horizontal_headers[self._anchor_index] + if len(self._horizontal_headers) != 0: + return self._horizontal_headers[self._anchor_index] + else: + return '' def rowCount(self, parent): return self._data.shape[0] @@ -1041,7 +1044,7 @@ def update(self, plot_data_collection, bd_option, interp_option, bd_plot): self._plot_data_collection = plot_data_collection self._data = np.zeros((len(seq_set) + 1, len(config_set))) - allowed_units = [("kbps","dB"),("kbps","s"),("kbps","VMAFScore")] + allowed_units = [("kbps","dB"),("kbps","s"),("kbps","VMAFScore"),("kbps","mos")] if all(collection.label in allowed_units for collection in plot_data_collection): self.update_table(bd_option, interp_option, 0, bd_plot) else: @@ -1118,7 +1121,7 @@ def update_table(self, bd_option, interp_option, anchor_index, bd_plot, ci_mode= # set the ci mode values for c1 ci1_mode = 'average' # no ci value available - if c1[0].ci: + if c1[0].has_ci: ci1_mode = ci_mode # sort the rd values for curve c1 @@ -1137,7 +1140,7 @@ def update_table(self, bd_option, interp_option, anchor_index, bd_plot, ci_mode= # set the ci mode values for c2 ci2_mode = 'average' # no ci value available - if c2[0].ci: + if c2[0].has_ci: ci2_mode = ci_mode # sort the rd values for curve c2 From 958c7160c6096bcba837a3d1dbb41b8768052415 Mon Sep 17 00:00:00 2001 From: Mirco Dilly Date: Mon, 23 Aug 2021 12:09:18 +0200 Subject: [PATCH 06/14] Clean up unit selection for csv files --- src/rdplot/SimulationDataItemClasses/CsvLogs.py | 8 ++++++-- src/rdplot/model.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/rdplot/SimulationDataItemClasses/CsvLogs.py b/src/rdplot/SimulationDataItemClasses/CsvLogs.py index cb84d91..c10df72 100644 --- a/src/rdplot/SimulationDataItemClasses/CsvLogs.py +++ b/src/rdplot/SimulationDataItemClasses/CsvLogs.py @@ -103,8 +103,12 @@ def _get_label(self, keys): :param keys: Variable/Path for which to get the labels :return: tuple of labels: (x-axis label, y-axis label) """ - if keys[1].find('psnr') != -1: - label = ('kbps','dB') + if keys[1].lower().find('psnr') != -1: + label = ('kbps', 'dB') + elif keys[1].lower().find('vmaf') != -1: + label = ("kbps", "VMAFScore") + elif keys[1].lower().find('mos') != -1: + label = ("kbps", "MOS") else: label = ('kbps', keys[1]) diff --git a/src/rdplot/model.py b/src/rdplot/model.py index 2510449..6efe22c 100644 --- a/src/rdplot/model.py +++ b/src/rdplot/model.py @@ -1044,7 +1044,7 @@ def update(self, plot_data_collection, bd_option, interp_option, bd_plot): self._plot_data_collection = plot_data_collection self._data = np.zeros((len(seq_set) + 1, len(config_set))) - allowed_units = [("kbps","dB"),("kbps","s"),("kbps","VMAFScore"),("kbps","mos")] + allowed_units = [("kbps","dB"),("kbps","s"),("kbps","VMAFScore"),("kbps","MOS")] if all(collection.label in allowed_units for collection in plot_data_collection): self.update_table(bd_option, interp_option, 0, bd_plot) else: From 69ddf7c17d8e97e52d1d89767fec39c05651be14 Mon Sep 17 00:00:00 2001 From: Mirco Dilly Date: Mon, 23 Aug 2021 12:10:24 +0200 Subject: [PATCH 07/14] Make ci settings persistent Don't change to average on change of selected sequence anymore --- src/rdplot/Widgets/MainWindow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rdplot/Widgets/MainWindow.py b/src/rdplot/Widgets/MainWindow.py index 16842ef..fbc1546 100644 --- a/src/rdplot/Widgets/MainWindow.py +++ b/src/rdplot/Widgets/MainWindow.py @@ -275,8 +275,8 @@ def change_list(self, q_selected, q_deselected): # overwriting keys is. self.selectedSimulationDataItemListModel.clear_and_update_from_tuples(tuples) - # reset the ci combo box if list selection changed - self.combo_ci.setCurrentIndex(0) + # update the bd table + self.update_bd_table(-1) def get_selected_simulation_data_items(self): return [self.selectedSimulationDataItemListModel[key] for key in self.selectedSimulationDataItemListModel] From 966750e44ee51aaaf043586cf74b3b864660b656 Mon Sep 17 00:00:00 2001 From: Mirco Dilly Date: Mon, 23 Aug 2021 13:02:07 +0200 Subject: [PATCH 08/14] Set status widget as hidden by default --- src/rdplot/Widgets/MainWindow.py | 3 ++ src/rdplot/ui/mainWindow.ui | 49 ++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/rdplot/Widgets/MainWindow.py b/src/rdplot/Widgets/MainWindow.py index fbc1546..afb466d 100644 --- a/src/rdplot/Widgets/MainWindow.py +++ b/src/rdplot/Widgets/MainWindow.py @@ -176,6 +176,9 @@ def __init__(self, ): self.simDataItemTreeView.customContextMenuRequested.connect(self.show_sequences_context_menu) # self.curveListView.actionCalculateBD.triggered.connect(self.bd_user_generated_curves) + # Set status Widget invisible as default + self.statusWidget.setVisible(False) + # sets Visibility for the Plotsettings Widget def set_plot_settings_visibility(self): self.plotsettings.visibilityChanged.disconnect(self.plot_settings_visibility_changed) diff --git a/src/rdplot/ui/mainWindow.ui b/src/rdplot/ui/mainWindow.ui index 8c96cca..c00a6af 100644 --- a/src/rdplot/ui/mainWindow.ui +++ b/src/rdplot/ui/mainWindow.ui @@ -7,7 +7,7 @@ 0 0 1253 - 784 + 782 @@ -209,7 +209,7 @@ padding: 4px; 200 - 251 + 329 @@ -239,6 +239,20 @@ padding: 4px; + + + + Qt::Horizontal + + + + + + + Bjontegaard Settings + + + @@ -250,19 +264,33 @@ padding: 4px; - - - true + + + Plot Bjontegaard - - + + + + + + Qt::Horizontal - + - Plot Bjontegaard + Confidence Interval Settings + + + + + + + true + + + @@ -463,6 +491,9 @@ padding: 4px; true + + true + &Hide Status From 71f14147de4364477bac90cc74ab6b6e743a0856 Mon Sep 17 00:00:00 2001 From: Mirco Dilly Date: Mon, 23 Aug 2021 20:16:52 +0200 Subject: [PATCH 09/14] Make ci mode persistent If only one curve is selected, reset the current ci mode back to 'average'. This is also done if one curve without ci interval is selected. --- src/rdplot/Widgets/MainWindow.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rdplot/Widgets/MainWindow.py b/src/rdplot/Widgets/MainWindow.py index afb466d..1e0ab4a 100644 --- a/src/rdplot/Widgets/MainWindow.py +++ b/src/rdplot/Widgets/MainWindow.py @@ -425,6 +425,8 @@ def update_plot(self): self.combo_ci.setEnabled(False) for plot_data in plot_data_collection: + if len(plot_data_collection) <= 1: + self.combo_ci.setCurrentIndex(0) if plot_data.has_ci and len(plot_data_collection) > 1: self.combo_ci.setEnabled(True) break From 3bdedc655560c0a86eba69887f9c5f2fbd8f8ec9 Mon Sep 17 00:00:00 2001 From: Mirco Dilly Date: Thu, 26 Aug 2021 08:43:44 +0200 Subject: [PATCH 10/14] Add ci intervals for user generated curves Add ci intervals and bjontegaard calculations for user generated curves. --- src/rdplot/Widgets/MainWindow.py | 50 +++++++++++++++++++++----------- src/rdplot/Widgets/PlotWidget.py | 6 ++-- src/rdplot/model.py | 22 ++++++++++---- 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/src/rdplot/Widgets/MainWindow.py b/src/rdplot/Widgets/MainWindow.py index 1e0ab4a..3c4a68f 100644 --- a/src/rdplot/Widgets/MainWindow.py +++ b/src/rdplot/Widgets/MainWindow.py @@ -412,7 +412,8 @@ def update_plot(self): # Update the anchor identifier for the plot preview which is selected by the # user in the bdTableModel by clicking on the header lines - self.plotPreview.anchor_identifier = self.bdTableModel.getAnchorIdentifier() + self.plotPreview.anchor_identifier = self.bdTableModel.getAnchorIdentifier() if len(data_collection_user_generated) == 0 else \ + self.bdUserGeneratedTableModel.getAnchorIdentifier() else: return @@ -626,14 +627,7 @@ def change_table_summary(self, plot_data_collect): data_count += 1 def update_doubleClicked_horizontalHeader(self, index): - # update the bd table (new anchor) - self.update_bd_table(index) - - # update the anchor identifier and ci mode for the ci plot view - self.plotPreview.anchor_identifier = self.bdTableModel.getAnchorIdentifier() - self.plotPreview.ci_mode = self.combo_ci.currentText() - - # update plot + # load the plot data collection self.check_labels() data_collection = self.get_plot_data_collection_from_selected_variables() data_collection_user_generated = [] @@ -642,6 +636,17 @@ def update_doubleClicked_horizontalHeader(self, index): plot_data_collection = data_collection + data_collection_user_generated + if len(data_collection_user_generated) > 0: + return + + # update the bd table (new anchor) + self.update_bd_table(index) + + # update the anchor identifier and ci mode for the ci plot view + self.plotPreview.anchor_identifier = self.bdTableModel.getAnchorIdentifier() + self.plotPreview.ci_mode = self.combo_ci.currentText() + + # update the table and plot self.plotPreview.tableView.setModel(self.bdTableModel) self.update_table(data_collection) self.plotPreview.change_plot(plot_data_collection, False) @@ -660,7 +665,7 @@ def update_bd_user_generated_curves_table(self, index): clicked_text = self.bdUserGeneratedTableModel.headerData(index, Qt.Vertical, Qt.DisplayRole) self.bdUserGeneratedTableModel.update(None, self.combo_rate_psnr.currentText(), self.combo_interp.currentText(), not(self.checkBox_bdplot.isChecked()), - clicked_text) + clicked_text, self.combo_ci.currentText()) def update_bd_plot(self): data_collection = self.get_plot_data_collection_from_selected_variables() @@ -709,13 +714,10 @@ def on_combo_box(self): self.update_bd_table(-1) def on_ci_combo_box(self): - # update the bd table (new ci mode) - self.update_bd_table(-1) - # update the ci mode for the ci plot view self.plotPreview.ci_mode = self.combo_ci.currentText() - # update plot + # load the plot data collection self.check_labels() data_collection = self.get_plot_data_collection_from_selected_variables() data_collection_user_generated = [] @@ -724,9 +726,23 @@ def on_ci_combo_box(self): plot_data_collection = data_collection + data_collection_user_generated - self.plotPreview.tableView.setModel(self.bdTableModel) - self.update_table(data_collection) - self.plotPreview.change_plot(plot_data_collection, False) + # Update tables and plots + if len(data_collection_user_generated) == 0: + self.bdTableModel.update_table(self.combo_rate_psnr.currentText(), + self.combo_interp.currentText(), -1, + not (self.checkBox_bdplot.isChecked()), + self.combo_ci.currentText()) + self.update_table(data_collection) + self.plotPreview.anchor_identifier = self.bdTableModel.getAnchorIdentifier() + self.plotPreview.change_plot(plot_data_collection, False) + else: + self.bdUserGeneratedTableModel.update_table(self.combo_rate_psnr.currentText(), + self.combo_interp.currentText(), -1, + not(self.checkBox_bdplot.isChecked()), + self.combo_ci.currentText()) + self.update_table(data_collection) + self.plotPreview.anchor_identifier = self.bdUserGeneratedTableModel.getAnchorIdentifier() + self.plotPreview.change_plot(plot_data_collection, True) def save_current_selection(self): """Saves the current selected sim data item collection""" diff --git a/src/rdplot/Widgets/PlotWidget.py b/src/rdplot/Widgets/PlotWidget.py index 54df7a3..f289774 100644 --- a/src/rdplot/Widgets/PlotWidget.py +++ b/src/rdplot/Widgets/PlotWidget.py @@ -207,15 +207,17 @@ def change_plot(self, plot_data_collection, user_generated_curves=False): xs_ci = np.concatenate((xs, xs[::-1])) # plot the curve (depending on ci mode) + anchor_index = 1 if not user_generated_curves else 0 + if self.ci_mode == 'average': curve = self.ax.plot(xs, ys, label=l) elif self.ci_mode == 'best': - if plot_data.identifiers[1] == self.anchor_identifier: + if plot_data.identifiers[anchor_index] == self.anchor_identifier: curve = self.ax.plot(xs, ys_low, label=l) else: curve = self.ax.plot(xs, ys_up, label=l) elif self.ci_mode == 'worst': - if plot_data.identifiers[1] == self.anchor_identifier: + if plot_data.identifiers[anchor_index] == self.anchor_identifier: curve = self.ax.plot(xs, ys_up, label=l) else: curve = self.ax.plot(xs, ys_low, label=l) diff --git a/src/rdplot/model.py b/src/rdplot/model.py index 6efe22c..6865891 100644 --- a/src/rdplot/model.py +++ b/src/rdplot/model.py @@ -1188,6 +1188,12 @@ class BdUserGeneratedCurvesTableModel(BdTableModel): def __init__(self): super().__init__() + def getAnchorIdentifier(self): + if len(self._horizontal_headers) != 0: + return self._plot_data_collection[self._anchor_index].identifiers[0] + else: + return '' + def data(self, q_index, role): if q_index.isValid() and role == Qt.DisplayRole: value = self._data[q_index.row(), q_index.column()] @@ -1197,13 +1203,15 @@ def data(self, q_index, role): return QVariant() def headerData(self, col, orientation, role): + if len(self._vertical_headers) == 0: + return QVariant() if orientation == Qt.Horizontal and role == Qt.DisplayRole: return QVariant(self._horizontal_headers) elif orientation == Qt.Vertical and role == Qt.DisplayRole: return QVariant(self._vertical_headers[col]) return QVariant() - def update(self, plot_data_collection, bd_option, interp_option, bd_plot, anchor_text=''): + def update(self, plot_data_collection, bd_option, interp_option, bd_plot, anchor_text='', ci_mode='average'): # reset the model in the first place and set data afterwards appropriately self.beginResetModel() self.reset_model() @@ -1262,15 +1270,16 @@ def update(self, plot_data_collection, bd_option, interp_option, bd_plot, anchor self._plot_data_collection = plot_data_collection self._data = np.zeros((len(self._vertical_headers), 1)) - if all(collection.label == ("kbps", "dB") for collection in plot_data_collection): - self.update_table(bd_option, interp_option, bd_plot) + allowed_units = [("kbps", "dB"), ("kbps", "s"), ("kbps", "VMAFScore"), ("kbps", "MOS")] + if all(collection.label in allowed_units for collection in plot_data_collection): + self.update_table(bd_option, interp_option, 0, bd_plot, ci_mode) else: self.beginResetModel() self.reset_model() self.endResetModel() plt.close() - def update_table(self, bd_option, interp_option, bd_plot): + def update_table(self, bd_option, interp_option, anchor_index, bd_plot, ci_mode='average'): # if there are no rows and columns in the model, # nothing can be updated if self.rowCount(self) == 0 and self.columnCount(self) == 0: @@ -1278,6 +1287,7 @@ def update_table(self, bd_option, interp_option, bd_plot): anchor_curve = self._plot_data_collection[self._anchor_index] c1 = sorted(list(set(anchor_curve.values))) + ci1_mode = ci_mode if anchor_curve.has_ci else 'average' index = 0 table_index = 0 curve_names = [self._horizontal_headers] @@ -1287,7 +1297,9 @@ def update_table(self, bd_option, interp_option, bd_plot): for curve in self._plot_data_collection: if index != self._anchor_index: c2 = sorted(list(set(curve.values))) - self._data[table_index, 0] = bjontegaard(c1, c2, bd_option, interp_option, 'BD Plot', curve_names, bd_plot) + ci2_mode = ci_mode if curve.has_ci else 'average' + self._data[table_index, 0] = bjontegaard(c1, c2, bd_option, interp_option, 'BD Plot', curve_names, bd_plot, + ci1_mode, ci2_mode) table_index += 1 index += 1 From 8cc336f3e7b79728ae570b03fb750a65faa371ad Mon Sep 17 00:00:00 2001 From: Mirco Dilly Date: Thu, 26 Aug 2021 08:57:57 +0200 Subject: [PATCH 11/14] Connect combo boxes for user generated curves Connect the bjontegaard settings combo boxes to user generated curves. --- src/rdplot/Widgets/MainWindow.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rdplot/Widgets/MainWindow.py b/src/rdplot/Widgets/MainWindow.py index 3c4a68f..a370bf4 100644 --- a/src/rdplot/Widgets/MainWindow.py +++ b/src/rdplot/Widgets/MainWindow.py @@ -710,8 +710,14 @@ def save_bd_table(self): self.bdTableModel.export_to_latex(filename) def on_combo_box(self): - # just update the bd table but do not change the anchor - self.update_bd_table(-1) + # check if user generated curves are available and update bd table + if self.curveListModel: + self.bdUserGeneratedTableModel.update_table(self.combo_rate_psnr.currentText(), + self.combo_interp.currentText(), -1, + not(self.checkBox_bdplot.isChecked()), + self.combo_ci.currentText()) + else: + self.update_bd_table(-1) def on_ci_combo_box(self): # update the ci mode for the ci plot view From 87457b990047a237594e707276f98b49bfb35758 Mon Sep 17 00:00:00 2001 From: Mirco Dilly Date: Thu, 2 Sep 2021 11:14:13 +0200 Subject: [PATCH 12/14] Code optimization --- src/rdplot/model.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/rdplot/model.py b/src/rdplot/model.py index 6865891..d0c137a 100644 --- a/src/rdplot/model.py +++ b/src/rdplot/model.py @@ -929,10 +929,7 @@ def __init__(self, parent=None, *args): self._plot_data_collection = [] def getAnchorIdentifier(self): - if len(self._horizontal_headers) != 0: - return self._horizontal_headers[self._anchor_index] - else: - return '' + return self._horizontal_headers[self._anchor_index] if len(self._horizontal_headers) else '' def rowCount(self, parent): return self._data.shape[0] @@ -1120,9 +1117,7 @@ def update_table(self, bd_option, interp_option, anchor_index, bd_plot, ci_mode= '+'.join(x.identifiers).__eq__('+'.join([identifiers_tmp[0], anchor]))] # set the ci mode values for c1 - ci1_mode = 'average' # no ci value available - if c1[0].has_ci: - ci1_mode = ci_mode + ci1_mode = ci_mode if c1[0].has_ci else 'average' # sort the rd values for curve c1 curve1 = sorted(list(set(c1[0].values))) # remove duplicates, this is just a workaround for the moment @@ -1139,9 +1134,7 @@ def update_table(self, bd_option, interp_option, anchor_index, bd_plot, ci_mode= if '+'.join(x.identifiers).__eq__('+'.join(identifiers_tmp))] # set the ci mode values for c2 - ci2_mode = 'average' # no ci value available - if c2[0].has_ci: - ci2_mode = ci_mode + ci2_mode = ci_mode if c2[0].has_ci else 'average' # sort the rd values for curve c2 curve2 = sorted(list(set(c2[0].values))) # remove duplicates, this is just a workaround for the moment @@ -1189,10 +1182,7 @@ def __init__(self): super().__init__() def getAnchorIdentifier(self): - if len(self._horizontal_headers) != 0: - return self._plot_data_collection[self._anchor_index].identifiers[0] - else: - return '' + return self._plot_data_collection[self._anchor_index].identifiers[0] if len(self._horizontal_headers) else '' def data(self, q_index, role): if q_index.isValid() and role == Qt.DisplayRole: From 344ee0de551941601d9cad0f9fc58ce0bbe7b761 Mon Sep 17 00:00:00 2001 From: Mirco Dilly Date: Thu, 2 Sep 2021 11:26:10 +0200 Subject: [PATCH 13/14] Make plot update consistent for user generated curves --- src/rdplot/Widgets/MainWindow.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/rdplot/Widgets/MainWindow.py b/src/rdplot/Widgets/MainWindow.py index a370bf4..a4d6f27 100644 --- a/src/rdplot/Widgets/MainWindow.py +++ b/src/rdplot/Widgets/MainWindow.py @@ -667,6 +667,17 @@ def update_bd_user_generated_curves_table(self, index): self.combo_interp.currentText(), not(self.checkBox_bdplot.isChecked()), clicked_text, self.combo_ci.currentText()) + # update the anchor identifier + self.plotPreview.anchor_identifier = self.bdUserGeneratedTableModel.getAnchorIdentifier() + + # load the user generated curves + data_collection_user_generated = [] + for index in self.curveListView.selectedIndexes(): + data_collection_user_generated.append(self.curveListModel[index.data()]) + + # update the plot + self.plotPreview.change_plot(data_collection_user_generated, True) + def update_bd_plot(self): data_collection = self.get_plot_data_collection_from_selected_variables() data_collection_user_generated = [] From 3bd110480e94bfb8518454ca3b8f9c150fc61379 Mon Sep 17 00:00:00 2001 From: Jens Schneider Date: Thu, 2 Sep 2021 15:39:17 +0200 Subject: [PATCH 14/14] Add exception handling when user generated curves are deselected This prevents RDPlot from crashing, when user generated curves with confidence interval are analyzed in worst-mode and deselected afterwards --- src/rdplot/model.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rdplot/model.py b/src/rdplot/model.py index d0c137a..b4c6246 100644 --- a/src/rdplot/model.py +++ b/src/rdplot/model.py @@ -950,7 +950,10 @@ def headerData(self, col, orientation, role): for c in tmp_horizontal_headers: result = list(filter(lambda x: all(x in l for l in tmp_horizontal_headers) == False, c)) headers.append(" ".join(result)) - return QVariant(headers[col]) + try: + return QVariant(headers[col]) + except (IndexError): + return QVariant() elif orientation == Qt.Vertical and role == Qt.DisplayRole: return QVariant(self._vertical_headers[col]) return QVariant()