From d6edc872c5259b34362884bddcec58360a525b73 Mon Sep 17 00:00:00 2001 From: Christopher Gantt <42154517+ganttArt@users.noreply.github.com> Date: Tue, 9 Oct 2018 15:36:47 -0700 Subject: [PATCH] ASDF Pixel Sort with ControlP5 GUI The original ASDF Pixel Sort algorithm with a new graphic interface so you can adjust variables, upload and save images in real time. --- .../ASDFPixelSort_ControlP5.pde | 502 ++++++++++++++++++ ASDFPixelSort_ControlP5/README.md | 12 + .../asdfPixelSortOpening.jpg | Bin 0 -> 12044 bytes 3 files changed, 514 insertions(+) create mode 100644 ASDFPixelSort_ControlP5/ASDFPixelSort_ControlP5.pde create mode 100644 ASDFPixelSort_ControlP5/README.md create mode 100644 ASDFPixelSort_ControlP5/asdfPixelSortOpening.jpg diff --git a/ASDFPixelSort_ControlP5/ASDFPixelSort_ControlP5.pde b/ASDFPixelSort_ControlP5/ASDFPixelSort_ControlP5.pde new file mode 100644 index 0000000..fccdb68 --- /dev/null +++ b/ASDFPixelSort_ControlP5/ASDFPixelSort_ControlP5.pde @@ -0,0 +1,502 @@ +/* ASDF Pixel Sort + Kim Asendorf | 2010 | kimasendorf.com + + sorting modes + 0 = black + 1 = brightness + 2 = white + */ + +import controlP5.*; +ControlP5 cp5; + +Toggle sortOrientationToggle; +boolean horizontallySorted; +Slider sortIntensitySlider; +int sort_intensity = -10000000; +RadioButton styleRadioButton; +int randomSortIntensity; +int randomSortOrientation; + +int mode = 0; + +PImage img; +PImage unsortedImage; +PImage savingImage; + +String filePath = ""; + +int loops = 1; + +int blackValue = sort_intensity; +int brightnessValue = sort_intensity; +int whiteValue = sort_intensity; + +int row = 0; +int column = 0; + +void setup() { + unsortedImage = img = loadImage("asdfPixelSortOpening.jpg"); + // use only numbers (not variables) for the size() command, Processing 3 + size(1, 1); + + PFont verdanaFont = createFont("Verdana", 11); + ControlFont font = new ControlFont(verdanaFont); + + // allow resize and update surface to image dimensions + surface.setResizable(true); + surface.setSize(img.width, img.height); + + // load image onto surface - scale to the available width,height for display + image(img, 0, 0, width, height); + + cp5 = new ControlP5(this); + cp5.setFont(font); + cp5.addButton("choose_image") + .setPosition(10, 10) + .setSize(100, 30) + .setLabel("choose image"); + + styleRadioButton = cp5.addRadioButton("style") + .setPosition(10, 50) + .setSize(40, 20) + .setSpacingRow(5) + .addItem("black", 0) + .addItem("brightness", 1) + .addItem("white", 2) + .activate("black"); + + sortOrientationToggle = cp5.addToggle("sort_orientation") + .setPosition(10, 130) + .setSize(60, 25) + .setMode(ControlP5.SWITCH) + .setLabel("orientation"); + + sortIntensitySlider = cp5.addSlider("sort_intensity") + .setLabel("") + .setSize(100, 30) + .setPosition(10, 180) + .setRange(-4000000, -16700000) + .setValue(-10000000); + + cp5.addTextlabel("label","SORT INTENSITY",7,213); + + cp5.addButton("random_sort") + .setPosition(10, 235) + .setSize(100, 30) + .setLabel("random sort"); + + cp5.addButton("save_image") + .setPosition(10, 275) + .setSize(90, 30) + .setLabel("save image"); +} +void draw() { + image(img, 0, 0, width, height); +} + +void resetImage() { + if (filePath == "") { + img = unsortedImage; + } else { + img = loadImage(filePath); + } + loops = 1; + blackValue = sort_intensity; + brightnessValue = sort_intensity; + whiteValue = sort_intensity; + row = 0; + column = 0; + surface.setSize(img.width, img.height); + image(img, 0, 0, width, height); +} + +public void choose_image() { + selectInput("Choose an image", "inputFile"); +} +void inputFile(File selected) { + filePath = selected.getAbsolutePath(); + unsortedImage = loadImage(filePath); + img = unsortedImage; + //reset variables + loops = 1; + blackValue = sort_intensity; + brightnessValue = sort_intensity; + whiteValue = sort_intensity; + row = 0; + column = 0; + // + surface.setSize(img.width, img.height); + image(img, 0, 0, width, height); + verticalSort(); + // sortOrientationToggle.setValue(false); +} + +void style(int a) { + resetImage(); + mode = a; + setSliderValues(mode); + if (sortOrientationToggle.getBooleanValue()) { + horizontalSort(); + } else { + verticalSort(); + } +} + +void sort_orientation(boolean horizontallySorted) { + resetImage(); + if (horizontallySorted==true) { + horizontalSort(); + } else { + verticalSort(); + } +} + +void controlEvent(ControlEvent theEvent) { + if (theEvent.isController()) { + if (theEvent.getController().getName()=="sort_intensity") { + resetImage(); + if (sortOrientationToggle.getBooleanValue()) { + horizontalSort(); + } else { + verticalSort(); + } + } + } +} + +void random_sort() { + mode = int(random(0, 3)); + if (mode==0) { + randomSortIntensity = int(random(-16700000, -4000000)); + styleRadioButton.activate("black"); + } + if (mode==1) { + randomSortIntensity = int(random(0, 200)); + styleRadioButton.activate("brightness"); + } + if (mode==2) { + randomSortIntensity = int(random(-8000000, -1)); + styleRadioButton.activate("white"); + } + sort_intensity = randomSortIntensity; + resetImage(); + setSliderValues(mode); + randomSortOrientation = int(random(2)); + if (randomSortOrientation==0) { + sortOrientationToggle.setValue(false); + horizontalSort(); + } else { + sortOrientationToggle.setValue(true); + verticalSort(); + } +} + +void setSliderValues(int mode) { + if (mode==0) { + sortIntensitySlider.setRange(-4000000, -16700000); + sortIntensitySlider.setValue(int(random(-16700000, -4000000))); + } + if (mode==1) { + sortIntensitySlider.setRange(200, 0); + sortIntensitySlider.setValue(int(random(0, 200))); + } + if (mode==2) { + sortIntensitySlider.setRange(-8000000, -1); + sortIntensitySlider.setValue(int(random(-8000000, -1))); + } +} + +void save_image() { + savingImage = get(0, 0, img.width, img.height); + selectOutput("Select a file to write to:", "fileSelected"); +} +void fileSelected(File selection) { + savingImage.save(selection.getAbsolutePath()+"."+month()+day()+hour()+minute()+second()+".jpg"); +} + + +//below is original ASDF Pixel Sort Algorithm +void horizontalSort() { + // loop through columns + while (column < img.width-1) { + img.loadPixels(); + sortColumn(); + column++; + img.updatePixels(); + } + // loop through rows + while (row < img.height-1) { + img.loadPixels(); + sortRow(); + row++; + img.updatePixels(); + } +} + +void verticalSort() { + while (row < img.height-1) { + img.loadPixels(); + sortRow(); + row++; + img.updatePixels(); + } + while (column < img.width-1) { + img.loadPixels(); + sortColumn(); + column++; + img.updatePixels(); + } +} + +void sortRow() { + // current row + int y = row; + + // where to start sorting + int x = 0; + + // where to stop sorting + int xend = 0; + + while (xend < img.width-1) { + switch(mode) { + case 0: + x = getFirstNotBlackX(x, y); + xend = getNextBlackX(x, y); + break; + case 1: + x = getFirstBrightX(x, y); + xend = getNextDarkX(x, y); + break; + case 2: + x = getFirstNotWhiteX(x, y); + xend = getNextWhiteX(x, y); + break; + default: + break; + } + + if (x < 0) break; + + int sortLength = xend-x; + + color[] unsorted = new color[sortLength]; + color[] sorted = new color[sortLength]; + + for (int i=0; i= img.width) + return -1; + } + + return x; +} + +int getNextBlackX(int x, int y) { + x++; + + while (img.pixels[x + y * img.width] > blackValue) { + x++; + if (x >= img.width) + return img.width-1; + } + + return x-1; +} + +// brightness x +int getFirstBrightX(int x, int y) { + + while (brightness(img.pixels[x + y * img.width]) < brightnessValue) { + x++; + if (x >= img.width) + return -1; + } + + return x; +} + +int getNextDarkX(int _x, int _y) { + int x = _x+1; + int y = _y; + + while (brightness(img.pixels[x + y * img.width]) > brightnessValue) { + x++; + if (x >= img.width) return img.width-1; + } + return x-1; +} + +// white x +int getFirstNotWhiteX(int x, int y) { + + while (img.pixels[x + y * img.width] > whiteValue) { + x++; + if (x >= img.width) + return -1; + } + return x; +} + +int getNextWhiteX(int x, int y) { + x++; + + while (img.pixels[x + y * img.width] < whiteValue) { + x++; + if (x >= img.width) + return img.width-1; + } + return x-1; +} + + +// black y +int getFirstNotBlackY(int x, int y) { + + if (y < img.height) { + while (img.pixels[x + y * img.width] < blackValue) { + y++; + if (y >= img.height) + return -1; + } + } + + return y; +} + +int getNextBlackY(int x, int y) { + y++; + + if (y < img.height) { + while (img.pixels[x + y * img.width] > blackValue) { + y++; + if (y >= img.height) + return img.height-1; + } + } + + return y-1; +} + +// brightness y +int getFirstBrightY(int x, int y) { + + if (y < img.height) { + while (brightness(img.pixels[x + y * img.width]) < brightnessValue) { + y++; + if (y >= img.height) + return -1; + } + } + + return y; +} + +int getNextDarkY(int x, int y) { + y++; + + if (y < img.height) { + while (brightness(img.pixels[x + y * img.width]) > brightnessValue) { + y++; + if (y >= img.height) + return img.height-1; + } + } + return y-1; +} + +// white y +int getFirstNotWhiteY(int x, int y) { + + if (y < img.height) { + while (img.pixels[x + y * img.width] > whiteValue) { + y++; + if (y >= img.height) + return -1; + } + } + + return y; +} + +int getNextWhiteY(int x, int y) { + y++; + + if (y < img.height) { + while (img.pixels[x + y * img.width] < whiteValue) { + y++; + if (y >= img.height) + return img.height-1; + } + } + + return y-1; +} diff --git a/ASDFPixelSort_ControlP5/README.md b/ASDFPixelSort_ControlP5/README.md new file mode 100644 index 0000000..86c1a13 --- /dev/null +++ b/ASDFPixelSort_ControlP5/README.md @@ -0,0 +1,12 @@ +ASDFPixelSort +============= + +Processing script to sort portions of pixels in an image. + +DEMO: +http://kimasendorf.com/mountain-tour/ +http://kimasendorf.com/sorted-aerial/ + + +Kim Asendorf 2010 +http://kimasendorf.com \ No newline at end of file diff --git a/ASDFPixelSort_ControlP5/asdfPixelSortOpening.jpg b/ASDFPixelSort_ControlP5/asdfPixelSortOpening.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5d737e986f775e8ab3cf6abb93fb4b1e57c8c3f0 GIT binary patch literal 12044 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 PlgxL$e4!ty2tWP{WX>CM literal 0 HcmV?d00001