User:Tom

From DigitalCraft_Wiki
Revision as of 18:50, 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.

\begin{lstlisting}

(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;
 }