js/src/parjs-benchmarks/liquid-resize.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 // -*- mode: js2; indent-tabs-mode: nil; -*-
michael@0 2
michael@0 3 // Adapted from
michael@0 4 //
michael@0 5 // https://github.com/RiverTrail/RiverTrail/blob/master/examples/liquid-resize/resize-compute-dp.js
michael@0 6 //
michael@0 7 // which in turn is based on an algorithm from the paper below (which
michael@0 8 // also appeared in ACM SIGGRAPH 2007):
michael@0 9 // Shai Avidan and Ariel Shamir. 2007. Seam carving for content-aware image resizing.
michael@0 10 // ACM Trans. Graph. 26, 3, Article 10 (July 2007).
michael@0 11 // DOI=10.1145/1276377.1276390 http://doi.acm.org/10.1145/1276377.1276390
michael@0 12
michael@0 13 ///////////////////////////////////////////////////////////////////////////
michael@0 14 // Inputs
michael@0 15
michael@0 16 function buildArray(width, height, func) {
michael@0 17 var length = width * height;
michael@0 18 var array = new Array(length);
michael@0 19 var index = 0;
michael@0 20 for (var y = 0; y < height; y++) {
michael@0 21 for (var x = 0; x < width; x++) {
michael@0 22 array[index++] = func(y, x);
michael@0 23 }
michael@0 24 }
michael@0 25 return array;
michael@0 26 }
michael@0 27
michael@0 28 function parImage(seqImage, width, height) {
michael@0 29 return new ParallelArray([height, width], function (y, x) {
michael@0 30 return seqImage[y*width + x];
michael@0 31 });
michael@0 32 }
michael@0 33
michael@0 34 var tinyImage =
michael@0 35 buildArray(20, 5, function(y, x) {
michael@0 36 var ret;
michael@0 37 if (6 <= x && x < 8 && 0 <= y && y < 4)
michael@0 38 ret = ".";
michael@0 39 else if ((x-15)*(x-15)+(y-1)*(y-1) < 2)
michael@0 40 ret = "^";
michael@0 41 else if ((x-20)*(x-20)+(y-3)*(y-3) < 2)
michael@0 42 ret = "%";
michael@0 43 else if ((x-1)*(x-1)+(y-3)*(y-3) < 2)
michael@0 44 ret = "@";
michael@0 45 else
michael@0 46 ret = " ";
michael@0 47 return ret.charCodeAt(0) - 32;
michael@0 48 });
michael@0 49
michael@0 50 var SmallImageWidth = 60;
michael@0 51 var SmallImageHeight = 15;
michael@0 52 var SmallImage =
michael@0 53 buildArray(SmallImageWidth, SmallImageHeight, function(y, x) {
michael@0 54 var ret;
michael@0 55 if (6 <= x && x < 8 && 0 <= y && y < 7)
michael@0 56 ret = ".";
michael@0 57 else if ((x-15)*(x-15)+(y-1)*(y-1) < 2)
michael@0 58 ret = "^";
michael@0 59 else if ((x-40)*(x-40)+(y-6)*(y-6) < 6)
michael@0 60 ret = "%";
michael@0 61 else if ((x-1)*(x-1)+(y-12)*(y-12) < 2)
michael@0 62 ret = "@";
michael@0 63 else
michael@0 64 ret = " ";
michael@0 65 return ret.charCodeAt(0) - 32;
michael@0 66 });
michael@0 67
michael@0 68 var SmallImagePar = parImage(SmallImage,
michael@0 69 SmallImageWidth,
michael@0 70 SmallImageHeight);
michael@0 71
michael@0 72 var bigImage =
michael@0 73 buildArray(200, 70, function(y, x) {
michael@0 74 var ret;
michael@0 75 if (4 <= x && x < 7 && 10 <= y && y < 40)
michael@0 76 ret = ".";
michael@0 77 else if ((x-150)*(x-150)+(y-13)*(y-13) < 70)
michael@0 78 ret = "^";
michael@0 79 else if ((x-201)*(x-201)+(y-33)*(y-33) < 200)
michael@0 80 ret = "%";
michael@0 81 else if ((x-15)*(x-15)+(y-3)*(y-3) < 7)
michael@0 82 ret = "@";
michael@0 83 else
michael@0 84 ret = " ";
michael@0 85 return ret.charCodeAt(0) - 32;
michael@0 86 });
michael@0 87
michael@0 88 // randomImage: Nat Nat Nat Nat -> RectArray
michael@0 89 function randomImage(w, h, sparsity, variety) {
michael@0 90 return buildArray(w, h, function (x,y) {
michael@0 91 if (Math.random() > 1/sparsity)
michael@0 92 return 0;
michael@0 93 else
michael@0 94 return 1+Math.random()*variety|0;
michael@0 95 });
michael@0 96 }
michael@0 97
michael@0 98 // stripedImage: Nat Nat -> RectArray
michael@0 99 function stripedImage(w, h) {
michael@0 100 return buildArray(w, h, function (y, x) {
michael@0 101 return (Math.abs(x%100-y%100) < 10) ? 32 : 0;
michael@0 102 });
michael@0 103 }
michael@0 104
michael@0 105 var massiveImage =
michael@0 106 buildArray(70, 10000, function(y, x) (Math.abs(x%100-y%100) < 10) ? 32 : 0);
michael@0 107
michael@0 108 function printImage(array, width, height) {
michael@0 109 print("Width", width, "Height", height);
michael@0 110 for (var y = 0; y < height; y++) {
michael@0 111 var line = "";
michael@0 112 for (var x = 0; x < width; x++) {
michael@0 113 var c = array[y*width + x];
michael@0 114 line += String.fromCharCode(c + 32);
michael@0 115 }
michael@0 116 print(line);
michael@0 117 }
michael@0 118 }
michael@0 119
michael@0 120 ///////////////////////////////////////////////////////////////////////////
michael@0 121 // Common
michael@0 122
michael@0 123 var SobelX = [[-1.0, 0.0, 1.0],
michael@0 124 [-2.0, 0.0, 2.0],
michael@0 125 [-1.0, 0.0, 1.0]];
michael@0 126 var SobelY = [[ 1.0, 2.0, 1.0],
michael@0 127 [ 0.0, 0.0, 0.0],
michael@0 128 [-1.0, -2.0, -1.0]];
michael@0 129
michael@0 130 // computeEnergy: Array -> RectArray
michael@0 131 //
michael@0 132 // (The return type is forced upon us, for now at least, until we add
michael@0 133 // appropriate API to ParallelArray; there's a dependency from each
michael@0 134 // row upon its predecessor, but the contents of each row could be
michael@0 135 // computed in parallel.)
michael@0 136 function computeEnergy(source, width, height) {
michael@0 137 var energy = new Array(width * height);
michael@0 138 energy[0] = 0;
michael@0 139 for (var y = 0; y < height; y++) {
michael@0 140 for (var x = 0; x < width; x++) {
michael@0 141 var e = source[y*width + x];
michael@0 142 if (y >= 1) {
michael@0 143 var p = energy[(y-1)*width + x];
michael@0 144 if (x > 0) {
michael@0 145 p = Math.min(p, energy[(y-1)*width + x-1]);
michael@0 146 }
michael@0 147 if (x < (width - 1)) {
michael@0 148 p = Math.min(p, energy[(y-1)*width + x+1]);
michael@0 149 }
michael@0 150 e += p;
michael@0 151 }
michael@0 152 energy[y*width + x] = e;
michael@0 153 }
michael@0 154 }
michael@0 155 return energy;
michael@0 156 }
michael@0 157
michael@0 158 // findPath: RectArray -> Array
michael@0 159 // (This is inherently sequential.)
michael@0 160 function findPath(energy, width, height)
michael@0 161 {
michael@0 162 var path = new Array(height);
michael@0 163 var y = height - 1;
michael@0 164 var minPos = 0;
michael@0 165 var minEnergy = energy[y*width + minPos];
michael@0 166
michael@0 167 for (var x = 1; x < width; x++) {
michael@0 168 if (energy[y+width + x] < minEnergy) {
michael@0 169 minEnergy = energy[y*width + x];
michael@0 170 minPos = x;
michael@0 171 }
michael@0 172 }
michael@0 173
michael@0 174 path[y] = minPos;
michael@0 175 for (y = height - 2; y >= 0; y--) {
michael@0 176 minEnergy = energy[y*width + minPos];
michael@0 177 // var line = energy[y]
michael@0 178 var p = minPos;
michael@0 179 if (p >= 1 && energy[y*width + p-1] < minEnergy) {
michael@0 180 minPos = p-1; minEnergy = energy[y*width + p-1];
michael@0 181 }
michael@0 182 if (p < width - 1 && energy[y*width + p+1] < minEnergy) {
michael@0 183 minPos = p+1; minEnergy = energy[y*width + p+1];
michael@0 184 }
michael@0 185 path[y] = minPos;
michael@0 186 }
michael@0 187 return path;
michael@0 188 }
michael@0 189
michael@0 190 ///////////////////////////////////////////////////////////////////////////
michael@0 191 // Sequential
michael@0 192
michael@0 193 function transposeSeq(array, width, height) {
michael@0 194 return buildArray(height, width, function(y, x) {
michael@0 195 return array[x*width + y];
michael@0 196 });
michael@0 197 }
michael@0 198
michael@0 199 // detectEdgesSeq: Array Nat Nat -> Array
michael@0 200 function detectEdgesSeq(data, width, height) {
michael@0 201 var data1 = new Array(width * height);
michael@0 202 for (var y = 0; y < height; y++) {
michael@0 203 for (var x = 0; x < width; x++) {
michael@0 204 var total = compute(y, x);
michael@0 205 data1[y*width + x] = total | 0;
michael@0 206 }
michael@0 207 }
michael@0 208 return data1;
michael@0 209
michael@0 210 function compute(y, x) {
michael@0 211 var totalX = 0;
michael@0 212 var totalY = 0;
michael@0 213
michael@0 214 var offYMin = (y == 0 ? 0 : -1);
michael@0 215 var offYMax = (y == height - 1 ? 0 : 1);
michael@0 216 var offXMin = (x == 0 ? 0 : -1);
michael@0 217 var offXMax = (x == width - 1 ? 0 : 1);
michael@0 218
michael@0 219 for (var offY = offYMin; offY <= offYMax; offY++) {
michael@0 220 for (var offX = offXMin; offX <= offXMax; offX++) {
michael@0 221 var e = data[(y + offY) * width + x + offX];
michael@0 222 totalX += e * SobelX[offY + 1][offX + 1];
michael@0 223 totalY += e * SobelY[offY + 1][offX + 1];
michael@0 224 }
michael@0 225 }
michael@0 226
michael@0 227 return (Math.abs(totalX) + Math.abs(totalY))/8.0 | 0;
michael@0 228 }
michael@0 229 }
michael@0 230
michael@0 231 function cutPathHorizontallyBWSeq(array, width, height, path) {
michael@0 232 return buildArray(width-1, height, function (y, x) {
michael@0 233 if (x < path[y]-1)
michael@0 234 return array[y*width + x];
michael@0 235 if (x == path[y]-1)
michael@0 236 return (array[y*width + x] + array[y*width + x+1]) / 2 | 0;
michael@0 237 else
michael@0 238 return array[y*width + x+1];
michael@0 239 });
michael@0 240 }
michael@0 241
michael@0 242 function cutPathVerticallyBWSeq(array, width, height, path) {
michael@0 243 return buildArray(width, height-1, function (y, x) {
michael@0 244 if (y < path[x]-1)
michael@0 245 return array[y*width + x];
michael@0 246 if (y == path[x]-1)
michael@0 247 return (array[y*width + x] + array[(y+1)*width + x]) / 2 | 0;
michael@0 248 else
michael@0 249 return array[(y+1)*width + x];
michael@0 250 });
michael@0 251 }
michael@0 252
michael@0 253 function cutHorizontalSeamBWSeq(array, width, height)
michael@0 254 {
michael@0 255 var edges = detectEdgesSeq(array, width, height);
michael@0 256 var energy = computeEnergy(edges, width, height);
michael@0 257 var path = findPath(energy, width, height);
michael@0 258 edges = null; // no longer live
michael@0 259 return cutPathHorizontallyBWSeq(array, width, height, path);
michael@0 260 }
michael@0 261
michael@0 262 function cutVerticalSeamBWSeq(array, width, height)
michael@0 263 {
michael@0 264 var arrayT = transposeSeq(array, width, height);
michael@0 265 var edges = detectEdgesSeq(arrayT, height, width);
michael@0 266 var energy = computeEnergy(edges, height, width);
michael@0 267 var path = findPath(energy, height, width);
michael@0 268 edges = null; // no longer live
michael@0 269 return cutPathVerticallyBWSeq(array, width, height, path);
michael@0 270 }
michael@0 271
michael@0 272 function reduceImageBWSeq(image,
michael@0 273 width, height,
michael@0 274 newWidth, newHeight,
michael@0 275 intermediateFunc,
michael@0 276 finalFunc) {
michael@0 277 while (width > newWidth || height > newHeight) {
michael@0 278 intermediateFunc(image, width, height);
michael@0 279
michael@0 280 if (width > newWidth) {
michael@0 281 image = cutHorizontalSeamBWSeq(image, width, height);
michael@0 282 width -= 1;
michael@0 283 }
michael@0 284
michael@0 285 if (height > newHeight) {
michael@0 286 image = cutVerticalSeamBWSeq(image, width, height);
michael@0 287 height -= 1;
michael@0 288 }
michael@0 289 }
michael@0 290
michael@0 291 finalFunc(image, width, height);
michael@0 292 return image;
michael@0 293 }
michael@0 294
michael@0 295 ///////////////////////////////////////////////////////////////////////////
michael@0 296 // Parallel
michael@0 297
michael@0 298 function transposePar(image) {
michael@0 299 var height = image.shape[0];
michael@0 300 var width = image.shape[1];
michael@0 301 return new ParallelArray([width, height], function (y, x) {
michael@0 302 return image.get(x, y);
michael@0 303 });
michael@0 304 }
michael@0 305
michael@0 306 // detectEdgesSeq: Array Nat Nat -> Array
michael@0 307 function detectEdgesPar(image) {
michael@0 308 var height = image.shape[0];
michael@0 309 var width = image.shape[1];
michael@0 310 return new ParallelArray([height, width], function (y, x) {
michael@0 311 var totalX = 0;
michael@0 312 var totalY = 0;
michael@0 313
michael@0 314 var offYMin = (y == 0 ? 0 : -1);
michael@0 315 var offYMax = (y == height - 1 ? 0 : 1);
michael@0 316 var offXMin = (x == 0 ? 0 : -1);
michael@0 317 var offXMax = (x == width - 1 ? 0 : 1);
michael@0 318
michael@0 319 for (var offY = offYMin; offY <= offYMax; offY++) {
michael@0 320 for (var offX = offXMin; offX <= offXMax; offX++) {
michael@0 321 var e = image.get(y + offY, x + offX);
michael@0 322 totalX += e * SobelX[offY + 1][offX + 1];
michael@0 323 totalY += e * SobelY[offY + 1][offX + 1];
michael@0 324 }
michael@0 325 }
michael@0 326
michael@0 327 var result = (Math.abs(totalX) + Math.abs(totalY))/8.0 | 0;
michael@0 328 return result;
michael@0 329 });
michael@0 330 }
michael@0 331
michael@0 332 function cutPathHorizontallyBWPar(image, path) {
michael@0 333 var height = image.shape[0];
michael@0 334 var width = image.shape[1];
michael@0 335 return new ParallelArray([height, width-1], function (y, x) {
michael@0 336 if (x < path[y]-1)
michael@0 337 return image.get(y, x);
michael@0 338 if (x == path[y]-1)
michael@0 339 return (image.get(y, x) + image.get(y, x+1)) / 2 | 0;
michael@0 340 else
michael@0 341 return image.get(y, x+1);
michael@0 342 });
michael@0 343 }
michael@0 344
michael@0 345 function cutPathVerticallyBWPar(image, path) {
michael@0 346 var height = image.shape[0];
michael@0 347 var width = image.shape[1];
michael@0 348 return new ParallelArray([height-1, width], function (y, x) {
michael@0 349 if (y < path[x]-1)
michael@0 350 return image.get(y, x);
michael@0 351 if (y == path[x]-1)
michael@0 352 return (image.get(y, x) + image.get(y+1, x)) / 2 | 0;
michael@0 353 else
michael@0 354 return image.get(y+1, x);
michael@0 355 });
michael@0 356 }
michael@0 357
michael@0 358 function cutHorizontalSeamBWPar(image)
michael@0 359 {
michael@0 360 var height = image.shape[0];
michael@0 361 var width = image.shape[1];
michael@0 362 var edges = detectEdgesPar(image);
michael@0 363 var energy = computeEnergy(edges.buffer, width, height);
michael@0 364 var path = findPath(energy, width, height);
michael@0 365 edges = null; // no longer live
michael@0 366 return cutPathHorizontallyBWPar(image, path);
michael@0 367 }
michael@0 368
michael@0 369 function cutVerticalSeamBWPar(image) {
michael@0 370 var height = image.shape[0];
michael@0 371 var width = image.shape[1];
michael@0 372 var imageT = transposePar(image);
michael@0 373 var edges = detectEdgesPar(imageT);
michael@0 374 var energy = computeEnergy(edges.buffer, height, width);
michael@0 375 var path = findPath(energy, height, width);
michael@0 376 edges = null; // no longer live
michael@0 377 return cutPathVerticallyBWPar(image, path);
michael@0 378 }
michael@0 379
michael@0 380 function reduceImageBWPar(image,
michael@0 381 newWidth, newHeight,
michael@0 382 intermediateFunc,
michael@0 383 finalFunc) {
michael@0 384 var height = image.shape[0];
michael@0 385 var width = image.shape[1];
michael@0 386 while (width > newWidth || height > newHeight) {
michael@0 387 intermediateFunc(image.buffer, width, height);
michael@0 388
michael@0 389 if (width > newWidth) {
michael@0 390 image = cutHorizontalSeamBWPar(image);
michael@0 391 width -= 1;
michael@0 392 }
michael@0 393
michael@0 394 if (height > newHeight) {
michael@0 395 image = cutVerticalSeamBWPar(image);
michael@0 396 height -= 1;
michael@0 397 }
michael@0 398 }
michael@0 399
michael@0 400 finalFunc(image.buffer, width, height);
michael@0 401 return image.buffer;
michael@0 402 }
michael@0 403
michael@0 404 ///////////////////////////////////////////////////////////////////////////
michael@0 405 // Benchmarking via run.sh
michael@0 406
michael@0 407 var BenchmarkImageWidth = 512;
michael@0 408 var BenchmarkImageHeight = 256;
michael@0 409 var BenchmarkImage = stripedImage(BenchmarkImageWidth,
michael@0 410 BenchmarkImageHeight);
michael@0 411 var BenchmarkImagePar = parImage(BenchmarkImage,
michael@0 412 BenchmarkImageWidth,
michael@0 413 BenchmarkImageHeight);
michael@0 414
michael@0 415 var benchmarking = (typeof(MODE) != "undefined");
michael@0 416 if (benchmarking) {
michael@0 417 load(libdir + "util.js");
michael@0 418 benchmark("LIQUID-RESIZE", 1, DEFAULT_MEASURE,
michael@0 419 function() {
michael@0 420 return reduceImageBWSeq(
michael@0 421 BenchmarkImage,
michael@0 422 BenchmarkImageWidth, BenchmarkImageHeight,
michael@0 423 BenchmarkImageWidth/2, BenchmarkImageHeight/2,
michael@0 424 function() {},
michael@0 425 function() {});
michael@0 426 },
michael@0 427
michael@0 428 function() {
michael@0 429 return reduceImageBWPar(
michael@0 430 BenchmarkImagePar,
michael@0 431 BenchmarkImageWidth/2, BenchmarkImageHeight/2,
michael@0 432 function() {},
michael@0 433 function() {});
michael@0 434 });
michael@0 435 }
michael@0 436
michael@0 437 ///////////////////////////////////////////////////////////////////////////
michael@0 438 // Running (sanity check)
michael@0 439
michael@0 440 if (!benchmarking) {
michael@0 441 var seqData =
michael@0 442 reduceImageBWSeq(SmallImage, SmallImageWidth, SmallImageHeight,
michael@0 443 SmallImageWidth - 15, SmallImageHeight - 10,
michael@0 444 function() {},
michael@0 445 printImage);
michael@0 446
michael@0 447 var parData =
michael@0 448 reduceImageBWPar(SmallImagePar,
michael@0 449 SmallImageWidth - 15, SmallImageHeight - 10,
michael@0 450 function() {},
michael@0 451 printImage);
michael@0 452
michael@0 453 var failed = false;
michael@0 454 assertEq(seqData.length, parData.length);
michael@0 455 for (var i = 0; i < seqData.length; i++) {
michael@0 456 if (seqData[i] !== parData[i]) {
michael@0 457 print("At index ", i, " sequential has ", seqData[i], " parallel has ", parData[i]);
michael@0 458 failed = true;
michael@0 459 }
michael@0 460 }
michael@0 461 if (failed)
michael@0 462 throw new Error();
michael@0 463 }

mercurial