Migration to GOGS repo.

master
ben-burlingham 10 years ago
commit f565821130
  1. 1
      .gitignore
  2. 25
      css/style.css
  3. BIN
      img/gothic-mona.png
  4. BIN
      img/gothic-scream.png
  5. BIN
      img/gothic-spheres.png
  6. BIN
      img/gothic-starry.png
  7. BIN
      img/gothic-stream.png
  8. BIN
      img/gothic.png
  9. BIN
      img/mona-gothic.png
  10. BIN
      img/mona-scream.png
  11. BIN
      img/mona-spheres.png
  12. BIN
      img/mona-starry.png
  13. BIN
      img/mona-stream.png
  14. BIN
      img/mona.png
  15. BIN
      img/noise-gothic.png
  16. BIN
      img/noise-scream.png
  17. BIN
      img/noise-spheres.png
  18. BIN
      img/noise-starry.png
  19. BIN
      img/noise-stream.png
  20. BIN
      img/noise.png
  21. BIN
      img/scream-gothic.png
  22. BIN
      img/scream-mona.png
  23. BIN
      img/scream-spheres.png
  24. BIN
      img/scream-starry.png
  25. BIN
      img/scream-stream.png
  26. BIN
      img/scream.png
  27. BIN
      img/spheres-gothic.png
  28. BIN
      img/spheres-mona.png
  29. BIN
      img/spheres-scream.png
  30. BIN
      img/spheres-starry.png
  31. BIN
      img/spheres-stream.png
  32. BIN
      img/spheres.png
  33. BIN
      img/starry-gothic.png
  34. BIN
      img/starry-mona.png
  35. BIN
      img/starry-scream.png
  36. BIN
      img/starry-spheres.png
  37. BIN
      img/starry-stream.png
  38. BIN
      img/starry.png
  39. BIN
      img/stream-gothic.png
  40. BIN
      img/stream-mona.png
  41. BIN
      img/stream-scream.png
  42. BIN
      img/stream-spheres.png
  43. BIN
      img/stream-starry.png
  44. BIN
      img/stream.png
  45. 377
      index.html
  46. 8
      package.json
  47. 211
      pixels.js

1
.gitignore vendored

@ -0,0 +1 @@
node_modules

@ -0,0 +1,25 @@
.problem-description {
background:#f5f2f0;
font-family:consolas;
line-height:24px;
padding:20px;
}
.results-matrix {
font-size:0;
text-align:center;
}
.results-matrix img {
border:3px solid #414141;
vertical-align:middle;
width: 100%;
}
.results-matrix a {
display:inline-block;
line-height:125px;
margin:5px 2px;
vertical-align:top;
width:110px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

@ -0,0 +1,377 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="/beb-2016/css/style.css">
<link rel="stylesheet" href="/beb-2016/css/prism.css">
</head>
<body>
<h1>Pixel Palettes</h1>
<!-- http://gogs.benburlingham.com/ben.burlingham/pixel-palettes -->
Tried my hand at <a href="http://codegolf.stackexchange.com/questions/33172/american-gothic-in-the-palette-of-mona-lisa-rearrange-the-pixels">this interesting challenge</a>:
<p class='problem-description'>
You are given two true color images, the Source and the Palette. They do not necessarily have the same dimensions but it is guaranteed that their areas are the same, i.e. they have the same number of pixels.
<br><br>
Your task is to create an algorithm that makes the most accurate looking copy of the Source by only using the pixels in the Palette. Each pixel in the Palette must be used exactly once in a unique position in this copy. The copy must have the same dimensions as the Source.
</p>
<p>
<a href="http://codegolf.stackexchange.com/questions/33172/american-gothic-in-the-palette-of-mona-lisa-rearrange-the-pixels/35960#35960">My solution</a> is not everything I wished, but it was a fun exercise.
</p>
<p>
The code basically takes a random pixel and uses a binary search to find a match in the target palette, weighted for the red component. Here are the results for that approach.
</p>
<h2>Results</h2>
<p>
Images are links, diagonals are originals.
</p>
<div class='results-matrix'>
<a href='/pixel-palettes/img/gothic.png' target='_new'>
<img src="/pixel-palettes/img/gothic.png" alt="Gothic original">
</a>
<a href='/pixel-palettes/img/gothic-mona.png' target='_new'>
<img src="/pixel-palettes/img/gothic-mona.png" alt="Gothic -> Mona Lisa">
</a>
<a href='/pixel-palettes/img/gothic-scream.png' target='_new'>
<img src="/pixel-palettes/img/gothic-scream.png" alt="Gothic -> Scream">
</a>
<a href='/pixel-palettes/img/gothic-spheres.png' target='_new'>
<img src="/pixel-palettes/img/gothic-spheres.png" alt="Gothic -> Spheres">
</a>
<a href='/pixel-palettes/img/gothic-starry.png' target='_new'>
<img src="/pixel-palettes/img/gothic-starry.png" alt="Gothic -> Starry Night">
</a>
<a href='/pixel-palettes/img/gothic-stream.png' target='_new'>
<img src="/pixel-palettes/img/gothic-stream.png" alt="Gothic -> Stream">
</a>
<a href='/pixel-palettes/img/mona-gothic.png' target='_new'>
<img src="/pixel-palettes/img/mona-gothic.png" alt="Mona -> Gothic">
</a>
<a href='/pixel-palettes/img/mona.png' target='_new'>
<img src="/pixel-palettes/img/mona.png" alt="Mona Lisa original">
</a>
<a href='/pixel-palettes/img/mona-scream.png' target='_new'>
<img src="/pixel-palettes/img/mona-scream.png" alt="Mona -> Scream">
</a>
<a href='/pixel-palettes/img/mona-spheres.png' target='_new'>
<img src="/pixel-palettes/img/mona-spheres.png" alt="Mona -> Spheres">
</a>
<a href='/pixel-palettes/img/mona-starry.png' target='_new'>
<img src="/pixel-palettes/img/mona-starry.png" alt="Mona -> Starry Night">
</a>
<a href='/pixel-palettes/img/mona-stream.png' target='_new'>
<img src="/pixel-palettes/img/mona-stream.png" alt="Mona -> Stream">
</a>
<a href='/pixel-palettes/img/scream-gothic.png' target='_new'>
<img src="/pixel-palettes/img/scream-gothic.png" alt="Scream -> Gothic">
</a>
<a href='/pixel-palettes/img/scream-mona.png' target='_new'>
<img src="/pixel-palettes/img/scream-mona.png" alt="Scream -> Mona Lisa">
</a>
<a href='/pixel-palettes/img/scream.png' target='_new'>
<img src="/pixel-palettes/img/scream.png" alt="Scream original">
</a>
<a href='/pixel-palettes/img/scream-spheres.png' target='_new'>
<img src="/pixel-palettes/img/scream-spheres.png" alt="Scream -> Spheres">
</a>
<a href='/pixel-palettes/img/scream-starry.png' target='_new'>
<img src="/pixel-palettes/img/scream-starry.png" alt="Scream -> Starry Night">
</a>
<a href='/pixel-palettes/img/scream-stream.png' target='_new'>
<img src="/pixel-palettes/img/scream-stream.png" alt="Scream -> Stream">
</a>
<a href='/pixel-palettes/img/spheres-gothic.png' target='_new'>
<img src="/pixel-palettes/img/spheres-gothic.png" alt="Spheres -> Gothic">
</a>
<a href='/pixel-palettes/img/spheres-mona.png' target='_new'>
<img src="/pixel-palettes/img/spheres-mona.png" alt="Spheres -> Mona Lisa">
</a>
<a href='/pixel-palettes/img/spheres-scream.png' target='_new'>
<img src="/pixel-palettes/img/spheres-scream.png" alt="Spheres -> Scream">
</a>
<a href='/pixel-palettes/img/spheres.png' target='_new'>
<img src="/pixel-palettes/img/spheres.png" alt="Spheres original">
</a>
<a href='/pixel-palettes/img/spheres-starry.png' target='_new'>
<img src="/pixel-palettes/img/spheres-starry.png" alt="Spheres -> Starry Night">
</a>
<a href='/pixel-palettes/img/spheres-stream.png' target='_new'>
<img src="/pixel-palettes/img/spheres-stream.png" alt="Spheres -> Stream">
</a>
<a href='/pixel-palettes/img/starry-gothic.png' target='_new'>
<img src="/pixel-palettes/img/starry-gothic.png" alt="Starry -> Gothic">
</a>
<a href='/pixel-palettes/img/starry-mona.png' target='_new'>
<img src="/pixel-palettes/img/starry-mona.png" alt="Starry -> Mona Lisa">
</a>
<a href='/pixel-palettes/img/starry-scream.png' target='_new'>
<img src="/pixel-palettes/img/starry-scream.png" alt="Starry -> Scream">
</a>
<a href='/pixel-palettes/img/starry-spheres.png' target='_new'>
<img src="/pixel-palettes/img/starry-spheres.png" alt="Starry -> Spheres">
</a>
<a href='/pixel-palettes/img/starry.png' target='_new'>
<img src="/pixel-palettes/img/starry.png" alt="Starry Night original">
</a>
<a href='/pixel-palettes/img/starry-stream.png' target='_new'>
<img src="/pixel-palettes/img/starry-stream.png" alt="Starry -> Stream">
</a>
<a href='/pixel-palettes/img/stream-gothic.png' target='_new'>
<img src="/pixel-palettes/img/stream-gothic.png" alt="Stream -> Gothic">
</a>
<a href='/pixel-palettes/img/stream-mona.png' target='_new'>
<img src="/pixel-palettes/img/stream-mona.png" alt="Stream -> Mona Lisa">
</a>
<a href='/pixel-palettes/img/stream-scream.png' target='_new'>
<img src="/pixel-palettes/img/stream-scream.png" alt="Stream -> Scream">
</a>
<a href='/pixel-palettes/img/stream-spheres.png' target='_new'>
<img src="/pixel-palettes/img/stream-spheres.png" alt="Stream -> Spheres">
</a>
<a href='/pixel-palettes/img/stream-starry.png' target='_new'>
<img src="/pixel-palettes/img/stream-starry.png" alt="Stream -> Starry Night">
</a>
<a href='/pixel-palettes/img/stream.png' target='_new'>
<img src="/pixel-palettes/img/stream.png" alt="Stream original">
</a>
</div>
<h2>Source code</h2>
<pre class='line-numbers'><code class='language-javascript'>var startTime = new Date().getTime();
var fs = require("fs");
var pngjs = require("pngjs").PNG;
/**
*
*/
var Pixels = function() {};
/**
*
*/
Pixels.prototype = {
source: null,
confirm: null,
target: null,
result: {},
};
/**
* Heavy lifting done here.
*/
Pixels.prototype.repalettize = function() {
// Indices is count from 0 to LxW, used to reference pixels in the tgtPalette, which are 8? bits wide.
var indices = [];
for (var y = 0; y &lt; P.target.height; y++) {
for (var x = 0; x &lt; P.target.width; x++) {
indices.push((P.target.width * y + x) &lt;&lt; 2);
}
}
var len = indices.length;
P.result.asBuffer = new Buffer(P.target.height * P.target.width * 4);
P.result.asArray = [];
var i, ii;
while (len) {
i = Math.floor(Math.random() * len);
ii = indices[i];
// Find RGB in source, no need for alpha channel.
matchIndex = P.findMatch(
zeropad(P.target.asBuffer[ii], 3) +
zeropad(P.target.asBuffer[ii + 1], 3) +
zeropad(P.target.asBuffer[ii + 2], 3)
);
matchRgb = P.source.asArray[matchIndex];
P.result.asArray.push(matchRgb);
P.result.asBuffer[ii] = matchRgb.substr(0, 3) * 1;
P.result.asBuffer[ii + 1] = matchRgb.substr(3, 3) * 1;
P.result.asBuffer[ii + 2] = matchRgb.substr(6, 3) * 1;
P.result.asBuffer[ii + 3] = 255;
indices.splice(i, 1);
P.source.asArray.splice(matchIndex, 1);
len = indices.length;
}
var resultImg = new pngjs({
filterType: 4
});
resultImg.data = P.result.asBuffer;
resultImg.width = P.target.width;
resultImg.height = P.target.height;
resultImg.pack().pipe(fs.createWriteStream('result.png'));
var endTime = new Date().getTime();
console.log((endTime - startTime) / 1000 + " seconds, confirming");
P.doConfirm(P.confirm.asArray, P.result.asArray) ?
console.log('OK - Source array and result array match.') :
console.log('ERROR! Source array and result array do not match!');
};
/**
* Slightly modified binary search tree.
*/
Pixels.prototype.findMatch = function(rgb0) {
var start = 0;
var end = P.source.asArray.length;
var mid;
while (start + 1 &lt; end) {
mid = Math.floor((end - start) / 2 + start);
if (P.source.asArray[mid] &lt; rgb0) {
start = mid;
}
else {
end = mid;
}
}
return start;
};
/**
*
*/
Pixels.prototype.doConfirm = function(arr1, arr2) {
var len1 = arr1.length;
var len2 = arr2.length;
if (len1 !== len2) {
return false;
}
arr1.sort();
arr2.sort();
for (var i = 0; i &lt; len1; i++) {
if (arr1[i] !== arr2[i]) {
return false;
}
}
return true;
};
/**
* Reads an image from a path, generates required information, and passes information to callback.
*/
Pixels.prototype.read = function(path0, callback0) {
var width = 0;
var height = 0;
var asBuffer = null;
var asArray = null;
fs.createReadStream(path0).pipe(new pngjs({ filterType: 4 }))
.on('metadata', function(meta0) {
width = meta0.width;
height = meta0.height;
})
.on('parsed', function(buffer0) {
var x, y, i;
var arr = [];
for (y = 0; y &lt; height; y++) {
for (x = 0; x &lt; width; x++) {
i = y * width + x &lt;&lt; 2;
arr.push(zeropad(buffer0[i], 3) + zeropad(buffer0[i + 1], 3) + zeropad(buffer0[i + 2], 3));
}
}
callback0({
width: width,
height: height,
asBuffer: buffer0,
asArray: arr,
});
});
};
/**
* sprintf implementation to ensure 9-digit pixels for sorting.
*/
var zeropad = function(str0, len0) {
str0 = str0.toString();
while (str0.length &lt; len0) {
str0 = "0" + str0;
}
return str0;
};
/**
* Ansynchronous file reads will execute and call this function. After they're all finished, the processing can begin.
*/
var filesRead = 0;
var thenContinue = function(data0) {
filesRead++;
if (filesRead === 3) {
P.source.asArray.sort();
P.repalettize();
}
}
/**
* Information for the source image, where the pixels are taken from.
*/
var thenSaveSource = function(obj0) {
P.source = obj0;
thenContinue();
}
/**
* A copy of the source data used after processing to ensure source pixels match result pixels.
*/
var thenSaveConfirm = function(obj0) {
P.confirm = obj0;
thenContinue();
}
/**
* Information for the target images, which the pixels are matched to.
*/
var thenSaveTarget = function(obj0) {
P.target = obj0;
thenContinue();
}
//===== Entry point
var P = new Pixels();
P.read('scream.png', thenSaveSource);
P.read('scream.png', thenSaveConfirm);
P.read('starry.png', thenSaveTarget);
</code></pre>
<script type="text/javascript" src='../beb-2016/js/ui.js'></script>
<script type="text/javascript" src='../beb-2016/js/prism.js'></script>
</body>
</html>

@ -0,0 +1,8 @@
{
"name": "pixels",
"version": "1.0.0",
"devDependencies": {
"chalk": "^0.5.1",
"pngjs": "^0.4.0"
}
}

@ -0,0 +1,211 @@
var startTime = new Date().getTime();
var fs = require("fs");
var pngjs = require("pngjs").PNG;
/**
*
*/
var Pixels = function() {};
/**
*
*/
Pixels.prototype = {
source: null,
confirm: null,
target: null,
result: {},
};
/**
* Heavy lifting done here.
*/
Pixels.prototype.repalettize = function() {
// Indices is count from 0 to LxW, used to reference pixels in the tgtPalette, which are 8? bits wide.
var indices = [];
for (var y = 0; y < P.target.height; y++) {
for (var x = 0; x < P.target.width; x++) {
indices.push((P.target.width * y + x) << 2);
}
}
var len = indices.length;
P.result.asBuffer = new Buffer(P.target.height * P.target.width * 4);
P.result.asArray = [];
var i, ii;
while (len) {
i = Math.floor(Math.random() * len);
ii = indices[i];
// Find RGB in source, no need for alpha channel.
matchIndex = P.findMatch(
zeropad(P.target.asBuffer[ii], 3) +
zeropad(P.target.asBuffer[ii + 1], 3) +
zeropad(P.target.asBuffer[ii + 2], 3)
);
matchRgb = P.source.asArray[matchIndex];
P.result.asArray.push(matchRgb);
P.result.asBuffer[ii] = matchRgb.substr(0, 3) * 1;
P.result.asBuffer[ii + 1] = matchRgb.substr(3, 3) * 1;
P.result.asBuffer[ii + 2] = matchRgb.substr(6, 3) * 1;
P.result.asBuffer[ii + 3] = 255;
indices.splice(i, 1);
P.source.asArray.splice(matchIndex, 1);
len = indices.length;
}
var resultImg = new pngjs({
filterType: 4
});
resultImg.data = P.result.asBuffer;
resultImg.width = P.target.width;
resultImg.height = P.target.height;
resultImg.pack().pipe(fs.createWriteStream('result.png'));
var endTime = new Date().getTime();
console.log((endTime - startTime) / 1000 + " seconds, confirming");
P.doConfirm(P.confirm.asArray, P.result.asArray) ?
console.log('OK - Source array and result array match.') :
console.log('ERROR! Source array and result array do not match!');
};
/**
* Slightly modified binary search tree.
*/
Pixels.prototype.findMatch = function(rgb0) {
var start = 0;
var end = P.source.asArray.length;
var mid;
while (start + 1 < end) {
mid = Math.floor((end - start) / 2 + start);
if (P.source.asArray[mid] < rgb0) {
start = mid;
}
else {
end = mid;
}
}
return start;
};
/**
*
*/
Pixels.prototype.doConfirm = function(arr1, arr2) {
var len1 = arr1.length;
var len2 = arr2.length;
if (len1 !== len2) {
return false;
}
arr1.sort();
arr2.sort();
for (var i = 0; i < len1; i++) {
if (arr1[i] !== arr2[i]) {
return false;
}
}
return true;
};
/**
* Reads an image from a path, generates required information, and passes information to callback.
*/
Pixels.prototype.read = function(path0, callback0) {
var width = 0;
var height = 0;
var asBuffer = null;
var asArray = null;
fs.createReadStream(path0).pipe(new pngjs({ filterType: 4 }))
.on('metadata', function(meta0) {
width = meta0.width;
height = meta0.height;
})
.on('parsed', function(buffer0) {
var x, y, i;
var arr = [];
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
i = y * width + x << 2;
arr.push(zeropad(buffer0[i], 3) + zeropad(buffer0[i + 1], 3) + zeropad(buffer0[i + 2], 3));
}
}
callback0({
width: width,
height: height,
asBuffer: buffer0,
asArray: arr,
});
});
};
/**
* sprintf implementation to ensure 9-digit pixels for sorting.
*/
var zeropad = function(str0, len0) {
str0 = str0.toString();
while (str0.length < len0) {
str0 = "0" + str0;
}
return str0;
};
/**
* Ansynchronous file reads will execute and call this function. After they're all finished, the processing can begin.
*/
var filesRead = 0;
var thenContinue = function(data0) {
filesRead++;
if (filesRead === 3) {
P.source.asArray.sort();
P.repalettize();
}
}
/**
* Information for the source image, where the pixels are taken from.
*/
var thenSaveSource = function(obj0) {
P.source = obj0;
thenContinue();
}
/**
* A copy of the source data used after processing to ensure source pixels match result pixels.
*/
var thenSaveConfirm = function(obj0) {
P.confirm = obj0;
thenContinue();
}
/**
* Information for the target images, which the pixels are matched to.
*/
var thenSaveTarget = function(obj0) {
P.target = obj0;
thenContinue();
}
//===== Entry point
var P = new Pixels();
P.read('scream.png', thenSaveSource);
P.read('scream.png', thenSaveConfirm);
P.read('starry.png', thenSaveTarget);
Loading…
Cancel
Save