User:Tom

From DigitalCraft_Wiki
Revision as of 18:44, 2 February 2018 by Tom (talk | contribs)
Jump to navigation Jump to search

Project (WORK IN PROGRESS)

I ran into a couple of gifs that visualised how certain sorting algorithms worked. Normally I don’t understand algorithms, but these visualisations made it so easy and insightful to understand what is going on. Besides that I think it’s cool to see an algorithm at work like this, I also think it has some artistic value. You could call it Glitch Art.

These are some examples of different codes working on a color palette:

PixelSortingExample.gif

It is a code creating a new kind of image by rearranging the original. This results in an image made anonymous. Or maybe more like a vague memory, where everything is still there but over time you start to forget what it looks like. This creates a new memory, a distorted version of the original.

Examplesort1.jpg Examplesort2.jpg
Examplesort3.jpg Examplesort4.jpg

What is Pixel Sorting?

Pixel Sorting is done with a program called Processing. It takes the pixels in a digital image and places them into a semblance of order. Pixel sorting was made popular by an artist of the name Kim Asendorf. He began sorting by developing a code in Processing.


Experiment

This is a modified version of the code written bij Kim Asendorf. It is written in Processing language, and requires a folder called ‘data’ in the same location as the .pde processing file. I used it to experiment with different images to discover what would happen.

PImage img;
PImage sorted;
int index = 0;

void setup() {
  size(800, 400);

  img = loadImage("afbeelding.jpg");
  sorted = createImage(img.width, img.height, RGB);
  sorted = img.get();
}

void draw() {
  println(frameRate);

  sorted.loadPixels();

  // Selection sort!
  for (int y = 0; y < sorted.height; y++) {

    float record = -1;
    int selectedPixel = index;

    for (int x = index; x < sorted.width; x++) {
      int loc = y * sorted.width + x;
      color pix = sorted.pixels[loc];
      float b = brightness(pix);
      if (b > record) {
        selectedPixel = loc;
        record = b;
      }
    }
    // Swap selectedPixel with i
    color temp = sorted.pixels[y * sorted.width + index];
    sorted.pixels[y * sorted.width + index] = sorted.pixels[selectedPixel];
    sorted.pixels[selectedPixel] = temp;
  }
  if (index < sorted.width -1) {
    index++;
  } else {
    save("sorted.jpg");
    frameRate(0);
  }

  sorted.updatePixels();

  background(0);
  image(img, 0, 0);
  image(sorted, 400, 0);
}

The code results in a pixel sorting visualisation:

Airplanesort.gif

The result was alright, only I had less control than I wanted to. I did research for some more codes and finally found one called 'Butter'. This code gave me a lot more control. I now had the option to sort the 'black', 'white' and 'bright' of the image.

(function () { var validModes = ['black', 'bright', 'white']; var defaultMode = validModes[0]; function Butter(mode, threshold) { this.mode = mode || defaultMode; if (validModes.indexOf(this.mode) === -1) { console.log('Butter has no mode called "' + this.mode + '".'); this.mode = defaultMode; } // defaults this.threshold = { black: -10000000, white: -6000000, bright: 30 }; if (typeof threshold !== 'undefined' && threshold !== null) { this.threshold[this.mode] = threshold; } } Butter.prototype.sort = function sort(canvas, iterations) { if (!canvas) { throw 'Butter needs a <canvas> to sort'; } var context = canvas.getContext('2d'), width = canvas.width, height = canvas.height, // Get the current data imageData = context.getImageData(0, 0, width, height), // And sort it sortedImage = this.sortImageData(imageData, width, height, iterations); context.putImageData(sortedImage, 0, 0); }; Butter.prototype.sortImageData = function sortImageData(imageData, width, height, iterations) { this.imageData = imageData; this.width = width; this.height = height; iterations || (iterations = 1); for (var i = 0; i < iterations; i++) { for (var column = 0; column < this.width; column++) { this.sortColumn(column); } for (var row = 0; row < this.height; row++) { this.sortRow(row); } } return this.imageData; }; Butter.prototype.setThreshold = function setThreshold(value) { this.threshold[this.mode] = value; }; Butter.prototype.sortColumn = function sortColumn(x) { var ranges = this.getRangesForColumn(x), range, width, pixelData; // For each range... for (var i = 0; i < ranges.length; i++) { range = ranges[i]; width = range.end - range.start; pixelData = new Array(width); // Get all the pixels in that range for (var j = 0; j < width; j++) { pixelData[j] = this.getPixelValue(x, range.start + j); } // Sort them! pixelData.sort(); // And put the new pixels back for (var j = 0; j < width; j++) { this.setPixelValue(x, (range.start + j), pixelData[j]); } } }; Butter.prototype.sortRow = function sortRow(y) { var ranges = this.getRangesForRow(y), range, width, pixelData; // For each range... for (var i = 0; i < ranges.length; i++) { range = ranges[i]; width = range.end - range.start; pixelData = new Array(width); // Get all the pixels in that range for (var j = 0; j < width; j++) { pixelData[j] = this.getPixelValue(range.start + j, y); } // Sort them! pixelData.sort(); // And put the new pixels back for (var j = 0; j < width; j++) { this.setPixelValue((range.start + j), y, pixelData[j]); } } }; Butter.prototype.getRangesForColumn = function getRangesForColumn(x) { var ranges = [], start = 0, end = 0, findFirst, findNext; switch(this.mode) { case 'black': findFirst = this.getFirstNotBlackY; findNext = this.getNextBlackY; break; case 'bright': findFirst = this.getFirstBrightY; findNext = this.getNextDarkY; break; case 'white': findFirst = this.getFirstNotWhiteY; findNext = this.getNextWhiteY; break; } for ( ; end < this.height; start = (end + 1)) { start = findFirst.call(this, x, start); end = findNext.call(this, x, start); // No more ranges if (start < 0 || start >= this.height) break; ranges.push({start: start, end: end}); } return ranges; }; Butter.prototype.getRangesForRow = function getRangesForRow(y) { var ranges = [], start = 0, end = 0, findFirst, findNext; switch(this.mode) { case 'black': findFirst = this.getFirstNotBlackX; findNext = this.getNextBlackX; break; case 'bright': findFirst = this.getFirstBrightX; findNext = this.getNextDarkX; break; case 'white': findFirst = this.getFirstNotWhiteX; findNext = this.getNextWhiteX; break; } for ( ; end < this.width; start = (end + 1)) { start = findFirst.call(this, start, y); end = findNext.call(this, start, y); // No more ranges if (start < 0 || start >= this.width) break; ranges.push({start: start, end: end}); } return ranges; }; /* Finders */ Butter.prototype.getFirstNotBlackX = function getFirstNotBlackX(x, y) { // Loop until we find a match for ( ; this.getPixelValue(x, y) < this.threshold.black; x++) { // Oh no, we've reached the edge! if (x >= this.width) return -1; } // Return the match return x; } Butter.prototype.getNextBlackX = function getNextBlackX(x, y) { // We want the _next_ one x += 1; for ( ; this.getPixelValue(x, y) > this.threshold.black; x++) { if (x >= this.width) return this.width - 1; } return x; } Butter.prototype.getFirstBrightX = function getFirstBrightX(x, y) { for ( ; this.getPixelBrightness(x, y) < this.threshold.bright; x++) { if (x >= this.width) return -1; } return x; } Butter.prototype.getNextDarkX = function getNextDarkX(x, y) { x += 1; for ( ; this.getPixelBrightness(x, y) > this.threshold.bright; x++) { if (x >= this.width) return this.width - 1; } return x; } Butter.prototype.getFirstNotWhiteX = function getFirstNotWhiteX(x, y) { for ( ; this.getPixelValue(x, y) > this.threshold.white; x++) { if (x >= this.width) return -1; } return x; } Butter.prototype.getNextWhiteX = function getNextWhiteX(x, y) { x += 1; for ( ; this.getPixelValue(x, y) < this.threshold.white; x++) { if (x >= this.width) return this.width - 1; } return x; } Butter.prototype.getFirstNotBlackY = function getFirstNotBlackY(x, y) { for ( ; this.getPixelValue(x, y) < this.threshold.black; y++) { if (y >= this.height) return -1; } return y; } Butter.prototype.getNextBlackY = function getNextBlackY(x, y) { y += 1; for ( ; this.getPixelValue(x, y) > this.threshold.black; y++) { if (y >= this.height) return this.height - 1; } return y; } Butter.prototype.getFirstBrightY = function getFirstBrightY(x, y) { for ( ; this.getPixelBrightness(x, y) < this.threshold.bright; y++) { if (y >= this.height) return -1; } return y; } Butter.prototype.getNextDarkY = function getNextDarkY(x, y) { y += 1; for ( ; this.getPixelBrightness(x, y) > this.threshold.bright; y++) { if (y >= this.height) return this.height - 1; } return y; } Butter.prototype.getFirstNotWhiteY = function getFirstNotWhiteY(x, y) { for ( ; this.getPixelValue(x, y) > this.threshold.white; y++) { if (y >= this.height) return -1; } return y; } Butter.prototype.getNextWhiteY = function getNextWhiteY(x, y) { y += 1; for ( ; this.getPixelValue(x, y) < this.threshold.white; y++) { if (y >= this.height) return this.height - 1; } return y; } /* Utilities */ Butter.prototype.getPixelOffset = function getPixelOffset(x, y) { return (x + y * this.width) * 4; }; Butter.prototype.setPixelValue = function setPixelValue(x, y, val) { var offset = this.getPixelOffset(x, y), r = (val >> 16) & 255, g = (val >> 8) & 255, b = val & 255, data = this.imageData.data; data[offset] = r; data[offset + 1] = g; data[offset + 2] = b; } Butter.prototype.getPixelValue = function getPixelValue(x, y) { var offset = this.getPixelOffset(x, y), data = this.imageData.data, r = data[offset], g = data[offset + 1], b = data[offset + 2]; return ( ((255 << 8) | r) << 8 | g) << 8 | b; } Butter.prototype.getPixelBrightness = function getPixelBrightness(x, y) { var offset = this.getPixelOffset(x, y), data = this.imageData.data, r = data[offset], g = data[offset + 1], b = data[offset + 2]; return Math.max(r, g, b) / 255 * 100; } // Let it loose if ((typeof module !== "undefined") && (module.exports)) { module.exports = Butter; } else { this.Butter = Butter; } }());