1function plotBenchmark(fileNames, export) 2%PLOTBENCHMARK Plots and exports video codec benchmarking results. 3% PLOTBENCHMARK(FILENAMES, EXPORT) parses the video codec benchmarking result 4% files given by the cell array of strings FILENAME. It plots the results and 5% optionally exports each plot to an appropriately named file. 6% 7% EXPORT parameter: 8% 'none' No file exports. 9% 'eps' Exports to eps files (default). 10% 'pdf' Exports to eps files and uses the command-line utility 11% epstopdf to obtain pdf files. 12% 13% Example: 14% plotBenchmark({'H264Benchmark.txt' 'LSVXBenchmark.txt'}, 'pdf') 15 16if (nargin < 1) 17 error('Too few input arguments'); 18elseif (nargin < 2) 19 export = 'eps'; 20end 21 22if ~iscell(fileNames) 23 if ischar(fileNames) 24 % one single file name as a string is ok 25 if size(fileNames,1) > 1 26 % this is a char matrix, not ok 27 error('First argument must not be a char matrix'); 28 end 29 % wrap in a cell array 30 fileNames = {fileNames}; 31 else 32 error('First argument must be a cell array of strings'); 33 end 34end 35 36if ~ischar(export) 37 error('Second argument must be a string'); 38end 39 40outpath = 'BenchmarkPlots'; 41[status, errMsg] = mkdir(outpath); 42if status == 0 43 error(errMsg); 44end 45 46nCases = 0; 47testCases = []; 48% Read each test result file 49for fileIdx = 1:length(fileNames) 50 if ~isstr(fileNames{fileIdx}) 51 error('First argument must be a cell array of strings'); 52 end 53 54 fid = fopen(fileNames{fileIdx}, 'rt'); 55 if fid == -1 56 error(['Unable to open ' fileNames{fileIdx}]); 57 end 58 59 version = '1.0'; 60 if ~strcmp(fgetl(fid), ['#!benchmark' version]) 61 fclose(fid); 62 error(['Requires benchmark file format version ' version]); 63 end 64 65 % Parse results file into testCases struct 66 codec = fgetl(fid); 67 tline = fgetl(fid); 68 while(tline ~= -1) 69 nCases = nCases + 1; 70 71 delim = strfind(tline, ','); 72 name = tline(1:delim(1)-1); 73 % Drop underscored suffix from name 74 underscore = strfind(name, '_'); 75 if ~isempty(underscore) 76 name = name(1:underscore(1)-1); 77 end 78 79 resolution = tline(delim(1)+1:delim(2)-1); 80 frameRate = tline(delim(2)+1:end); 81 82 tline = fgetl(fid); 83 delim = strfind(tline, ','); 84 bitrateLabel = tline(1:delim(1)-1); 85 bitrate = sscanf(tline(delim(1):end),',%f'); 86 87 tline = fgetl(fid); 88 delim = strfind(tline, ','); 89 psnrLabel = tline(1:delim(1)-1); 90 psnr = sscanf(tline(delim(1):end),',%f'); 91 92 93 % Default data for the optional lines 94 speedLabel = 'Default'; 95 speed = 0; 96 ssimLabel = 'Default'; 97 ssim = 0; 98 99 tline = fgetl(fid); 100 delim = strfind(tline, ','); 101 102 while ~isempty(delim) 103 % More data 104 % Check type of data 105 if strncmp(lower(tline), 'speed', 5) 106 % Speed data included 107 speedLabel = tline(1:delim(1)-1); 108 speed = sscanf(tline(delim(1):end), ',%f'); 109 110 tline = fgetl(fid); 111 112 elseif strncmp(lower(tline), 'encode time', 11) 113 % Encode and decode times included 114 % TODO: take care of the data 115 116 % pop two lines from file 117 tline = fgetl(fid); 118 tline = fgetl(fid); 119 120 elseif strncmp(tline, 'SSIM', 4) 121 % SSIM data included 122 ssimLabel = tline(1:delim(1)-1); 123 ssim = sscanf(tline(delim(1):end), ',%f'); 124 125 tline = fgetl(fid); 126 end 127 delim = strfind(tline, ','); 128 end 129 130 testCases = [testCases struct('codec', codec, 'name', name, 'resolution', ... 131 resolution, 'frameRate', frameRate, 'bitrate', bitrate, 'psnr', psnr, ... 132 'speed', speed, 'bitrateLabel', bitrateLabel, 'psnrLabel', psnrLabel, ... 133 'speedLabel', speedLabel, ... 134 'ssim', ssim, 'ssimLabel', ssimLabel)]; 135 136 tline = fgetl(fid); 137 end 138 139 fclose(fid); 140end 141 142i = 0; 143casesPsnr = testCases; 144while ~isempty(casesPsnr) 145 i = i + 1; 146 casesPsnr = plotOnePsnr(casesPsnr, i, export, outpath); 147end 148 149casesSSIM = testCases; 150while ~isempty(casesSSIM) 151 i = i + 1; 152 casesSSIM = plotOneSSIM(casesSSIM, i, export, outpath); 153end 154 155casesSpeed = testCases; 156while ~isempty(casesSpeed) 157 if casesSpeed(1).speed == 0 158 casesSpeed = casesSpeed(2:end); 159 else 160 i = i + 1; 161 casesSpeed = plotOneSpeed(casesSpeed, i, export, outpath); 162 end 163end 164 165 166 167%%%%%%%%%%%%%%%%%% 168%% SUBFUNCTIONS %% 169%%%%%%%%%%%%%%%%%% 170 171function casesOut = plotOnePsnr(cases, num, export, outpath) 172% Find matching specs 173plotIdx = 1; 174for i = 2:length(cases) 175 if strcmp(cases(1).resolution, cases(i).resolution) & ... 176 strcmp(cases(1).frameRate, cases(i).frameRate) 177 plotIdx = [plotIdx i]; 178 end 179end 180 181% Return unplotted cases 182casesOut = cases(setdiff(1:length(cases), plotIdx)); 183cases = cases(plotIdx); 184 185% Prune similar results 186for i = 1:length(cases) 187 simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10); 188 while ~isempty(simIndx) 189 diffIndx = setdiff(1:length(cases(i).bitrate), simIndx); 190 cases(i).psnr = cases(i).psnr(diffIndx); 191 cases(i).bitrate = cases(i).bitrate(diffIndx); 192 simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10); 193 end 194end 195 196% Prepare figure with axis labels and so on 197hFig = figure(num); 198clf; 199hold on; 200grid on; 201axis([0 1100 20 50]); 202set(gca, 'XTick', 0:200:1000); 203set(gca, 'YTick', 20:10:60); 204xlabel(cases(1).bitrateLabel); 205ylabel(cases(1).psnrLabel); 206res = cases(1).resolution; 207frRate = cases(1).frameRate; 208title([res ', ' frRate]); 209 210hLines = []; 211codecs = {}; 212sequences = {}; 213i = 0; 214while ~isempty(cases) 215 i = i + 1; 216 [cases, hLine, codec, sequences] = plotOneCodec(cases, 'bitrate', 'psnr', i, sequences, 1); 217 218 % Stored to generate the legend 219 hLines = [hLines ; hLine]; 220 codecs = {codecs{:} codec}; 221end 222legend(hLines, codecs, 4); 223hold off; 224 225if ~strcmp(export, 'none') 226 % Export figure to an eps file 227 res = stripws(res); 228 frRate = stripws(frRate); 229 exportName = [outpath '/psnr-' res '-' frRate]; 230 exportfig(hFig, exportName, 'Format', 'eps2', 'Color', 'cmyk'); 231end 232 233if strcmp(export, 'pdf') 234 % Use the epstopdf utility to convert to pdf 235 system(['epstopdf ' exportName '.eps']); 236end 237 238 239function casesOut = plotOneSSIM(cases, num, export, outpath) 240% Find matching specs 241plotIdx = 1; 242for i = 2:length(cases) 243 if strcmp(cases(1).resolution, cases(i).resolution) & ... 244 strcmp(cases(1).frameRate, cases(i).frameRate) 245 plotIdx = [plotIdx i]; 246 end 247end 248 249% Return unplotted cases 250casesOut = cases(setdiff(1:length(cases), plotIdx)); 251cases = cases(plotIdx); 252 253% Prune similar results 254for i = 1:length(cases) 255 simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10); 256 while ~isempty(simIndx) 257 diffIndx = setdiff(1:length(cases(i).bitrate), simIndx); 258 cases(i).ssim = cases(i).ssim(diffIndx); 259 cases(i).bitrate = cases(i).bitrate(diffIndx); 260 simIndx = find(abs(cases(i).bitrate - [cases(i).bitrate(2:end) ; 0]) < 10); 261 end 262end 263 264% Prepare figure with axis labels and so on 265hFig = figure(num); 266clf; 267hold on; 268grid on; 269axis([0 1100 0.5 1]); % y-limit are set to 'auto' below 270set(gca, 'XTick', 0:200:1000); 271%set(gca, 'YTick', 20:10:60); 272xlabel(cases(1).bitrateLabel); 273ylabel(cases(1).ssimLabel); 274res = cases(1).resolution; 275frRate = cases(1).frameRate; 276title([res ', ' frRate]); 277 278hLines = []; 279codecs = {}; 280sequences = {}; 281i = 0; 282while ~isempty(cases) 283 i = i + 1; 284 [cases, hLine, codec, sequences] = plotOneCodec(cases, 'bitrate', 'ssim', i, sequences, 1); 285 286 % Stored to generate the legend 287 hLines = [hLines ; hLine]; 288 codecs = {codecs{:} codec}; 289end 290%set(gca,'YLimMode','auto') 291set(gca,'YLim',[0.5 1]) 292set(gca,'YScale','log') 293legend(hLines, codecs, 4); 294hold off; 295 296if ~strcmp(export, 'none') 297 % Export figure to an eps file 298 res = stripws(res); 299 frRate = stripws(frRate); 300 exportName = [outpath '/psnr-' res '-' frRate]; 301 exportfig(hFig, exportName, 'Format', 'eps2', 'Color', 'cmyk'); 302end 303 304if strcmp(export, 'pdf') 305 % Use the epstopdf utility to convert to pdf 306 system(['epstopdf ' exportName '.eps']); 307end 308 309 310function casesOut = plotOneSpeed(cases, num, export, outpath) 311% Find matching specs 312plotIdx = 1; 313for i = 2:length(cases) 314 if strcmp(cases(1).resolution, cases(i).resolution) & ... 315 strcmp(cases(1).frameRate, cases(i).frameRate) & ... 316 strcmp(cases(1).name, cases(i).name) 317 plotIdx = [plotIdx i]; 318 end 319end 320 321% Return unplotted cases 322casesOut = cases(setdiff(1:length(cases), plotIdx)); 323cases = cases(plotIdx); 324 325% Prune similar results 326for i = 1:length(cases) 327 simIndx = find(abs(cases(i).psnr - [cases(i).psnr(2:end) ; 0]) < 0.25); 328 while ~isempty(simIndx) 329 diffIndx = setdiff(1:length(cases(i).psnr), simIndx); 330 cases(i).psnr = cases(i).psnr(diffIndx); 331 cases(i).speed = cases(i).speed(diffIndx); 332 simIndx = find(abs(cases(i).psnr - [cases(i).psnr(2:end) ; 0]) < 0.25); 333 end 334end 335 336hFig = figure(num); 337clf; 338hold on; 339%grid on; 340xlabel(cases(1).psnrLabel); 341ylabel(cases(1).speedLabel); 342res = cases(1).resolution; 343name = cases(1).name; 344frRate = cases(1).frameRate; 345title([name ', ' res ', ' frRate]); 346 347hLines = []; 348codecs = {}; 349sequences = {}; 350i = 0; 351while ~isempty(cases) 352 i = i + 1; 353 [cases, hLine, codec, sequences] = plotOneCodec(cases, 'psnr', 'speed', i, sequences, 0); 354 355 % Stored to generate the legend 356 hLines = [hLines ; hLine]; 357 codecs = {codecs{:} codec}; 358end 359legend(hLines, codecs, 1); 360hold off; 361 362if ~strcmp(export, 'none') 363 % Export figure to an eps file 364 res = stripws(res); 365 frRate = stripws(frRate); 366 exportName = [outpath '/speed-' name '-' res '-' frRate]; 367 exportfig(hFig, exportName, 'Format', 'eps2', 'Color', 'cmyk'); 368end 369 370if strcmp(export, 'pdf') 371 % Use the epstopdf utility to convert to pdf 372 system(['epstopdf ' exportName '.eps']); 373end 374 375 376function [casesOut, hLine, codec, sequences] = plotOneCodec(cases, xfield, yfield, num, sequences, annotatePlot) 377plotStr = {'gx-', 'bo-', 'r^-', 'kd-', 'cx-', 'go--', 'b^--'}; 378% Find matching codecs 379plotIdx = 1; 380for i = 2:length(cases) 381 if strcmp(cases(1).codec, cases(i).codec) 382 plotIdx = [plotIdx i]; 383 end 384end 385 386% Return unplotted cases 387casesOut = cases(setdiff(1:length(cases), plotIdx)); 388cases = cases(plotIdx); 389 390for i = 1:length(cases) 391 % Plot a single case 392 hLine = plot(getfield(cases(i), xfield), getfield(cases(i), yfield), plotStr{num}, ... 393 'LineWidth', 1.1, 'MarkerSize', 6); 394end 395 396% hLine handle and codec are returned to construct the legend afterwards 397codec = cases(1).codec; 398 399if annotatePlot == 0 400 return; 401end 402 403for i = 1:length(cases) 404 % Print the codec name as a text label 405 % Ensure each codec is only printed once 406 sequencePlotted = 0; 407 for j = 1:length(sequences) 408 if strcmp(cases(i).name, sequences{j}) 409 sequencePlotted = 1; 410 break; 411 end 412 end 413 414 if sequencePlotted == 0 415 text(getfield(cases(i), xfield, {1}), getfield(cases(i), yfield, {1}), ... 416 [' ' cases(i).name]); 417 sequences = {sequences{:} cases(i).name}; 418 end 419end 420 421 422% Strip whitespace from string 423function str = stripws(str) 424if ~isstr(str) 425 error('String required'); 426end 427str = str(setdiff(1:length(str), find(isspace(str) == 1))); 428