User:Tom

From DigitalCraft_Wiki
Jump to navigation Jump to search

Pixel Sorting

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

Processing

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.

Butter

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

This code gave me a lot more control. For instance with the old Apple universe background image:

Apple.jpg Apple1.png Apple2.png Apple3.png

Other Experiments

These are experiments i tried with simpler codes.

Favicon.png

Sorted horizontally from dark to light colors.

Favicon Sort.png

1.png

2.png

3ico.png

4ico.png

Former Glory: Logo's & Icons

As I mentioned before, the result of pixel sorting reminds me of some distant memory, distorted over time. I want to recreate this feeling in a series of images.

First I needed something that counts as some sort of memory, something recognisable. Second, it had to be recognisable for a larger audience. So I had to find some sort of 'collective memory'. For this I chose to use iconic logo's of worldwide brands like McDonalds or Coca Cola. These brands are known worldwide and have been around for many years. Chances are high people will recognise them, even when the logos appear distorted.

Chanel

ChanelLogo.jpg

Sorted + Contact Sheet

Click to enlarge!!!

ChanelLogo6.png
ChanelSheet.jpg

Coca Cola

Coca Cola logo.jpg

Sorted + Contact Sheet

Click to enlarge!!!

CocaColaLogo17.png
CocaColaSheet.jpg

Marlboro

MarlboroLogo.jpg

Sorted + Contact Sheet

Click to enlarge!!!

MarlboroLogo2.png
MarlboroSheet.jpg

Mcdonalds

McdonaldsLogo.png

Sorted + Contact Sheet

Click to enlarge!!!

McDonaldsLogo21.png
McDonaldsSheet.jpg

Pepsi

PepsiLogo.jpg

Sorted + Contact Sheet

Click to enlarge!!!

PepsiLogo3.png
PepsiSheet.jpg

Starbucks

StarbucksLogo.png

Sorted + Contact Sheet

Click to enlarge!!!

Starbuckslogo14.png
StarbucksSheet.jpg

Volkswagen

VolkswagenLogo.png

Sorted + Contact Sheet

Click to enlarge!!!

VolkswagenLogo20.png
VolkswagenSheet.jpg