Madeline Gannon-Project 2-Final Post

by Madeline Gannon @ 5:17 am 26 January 2011

Remembering Velázquez | The Exquisite Corpse of Western Art

This data visualization was inspired by the 2008 exhibit Oblidant Velázquez, or Forgetting Velázquez. The exhibition focused on the lasting impact of Diego Velázquez’s baroque masterpiece ‘Las Meninas’ on the collective psyche of western art. Barcelona’s Museu Picasso curated several dozen well known variations and interpretations of Las Meninas, displaying them along side the original in a transhistorical dialogue between master artists.

Velázquez's 1656 version (left) with a few twentieth century interpretations (right)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

I was also inspired by the work of Jason Salavon’s 100 Special Moments portraiture, in which the recording of the most important moments of one’s life (wedding, graduation, etc.) is shown to be generic and ordinary through a image processing technique called Image Averaging:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

With Velázquez’s Las Meninas, I saw the various iterations as layers of social and cultural understanding/commentary of each artist’s Zeitgeist. They seemed to me to be analogous to an MRI scan sectioning through ones body, revealing the compositional core that make up the body as a whole.

My visualization attempts to answer the question Can recompositing these layers create an exquisite corpse of these collective works?

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Source Code for this project:

/*
Madeline Gannon
  Remembering Velazquez
  IACD | 1.26.2011
 
  Image Averaging Code based
  on work by Jason Salavon
*/
 
PFont palatinoB;
PFont palatinoI;
PFont palatinoR;
PFont palatinoBI;
 
int offset = 30;
int btnCount =  5;
Radio[] buttons = new Radio[btnCount];
 
 
PImage b;
PImage images[];
int nImages = 0;
int nPixels;
float averageImagef [][];
color averagedPixelColors[];
 
void setup() {
  size(694, 800);
  smooth();
  b = loadImage("Las Meninas_velazquez.png");
  background(b);
  //
  palatinoB  = loadFont("PalatinoLinotype-Bold-48.vlw");
  palatinoI  = loadFont("PalatinoLinotype-Italic-48.vlw");
  palatinoR  = loadFont("PalatinoLinotype-Roman-48.vlw");
  palatinoBI = loadFont("PalatinoLinotype-BoldItalic-48.vlw");
 
  // place buttons
  for (int i=0; i<btnCount; i++) {
    int x = ((int)((width) * (float)(i+1)/(btnCount+1)))-55;
    int y = height-15;
    // Btn inputs: x, y, size, base color, fill color, array
    buttons[i] = new Radio(x, y, 15, color(111,138,152), color(245,214,91),
    i, buttons);
  }
 
  nPixels = (width)*(height);
  initializeArrays();
}
 
 
 
void draw() {
  /////////////////////
  // Image Averaging //
  /////////////////////
  delay(100);
  loadPixels();
  for (int i=0; i<nPixels; i++) {
    pixels[i] = averagedPixelColors[i];
  } 
  updatePixels();
 
  // put a transparent box around btns
  color c = color(255, 255, 255, 100);
  fill(c);
  noStroke();
  rect(45, 770, 625, 30);
 
  // Draw buttons
  for (int i=0; i<btnCount; i++) {
    buttons[i].display();
  }
  // Display Text
  displayText();
}
 
 
/////////////////////////////////////////////////////////
////////             MOUSE PRESSED               ////////
/////////////////////////////////////////////////////////
 
void mousePressed() {
  System.out.println("mouseX = "+mouseX);
  System.out.println("mouseY = "+mouseY);
  for (int i=0; i<btnCount; i++) {
    buttons[i].press(mouseX, mouseY);
  }
  // btn 0
  if (mouseY>777 && mouseY<793 && mouseX>52 && mouseX<68) {       
    initializeArrays();
    loadAll();
    computeAverageImage();
  } 
  // btn 1
  if (mouseY>777 && mouseY<793 && mouseX>169 && mouseX<183) {   
    initializeArrays();
    loadOld();
    computeAverageImage();
  }
  // btn 2
  if (mouseY>777 && mouseY<793 && mouseX>283 && mouseX<300) {     
    initializeArrays();
    loadModern();
    computeAverageImage();
  }
  // btn 3
  if (mouseY>777 && mouseY<793 && mouseX>399 && mouseX<414) {    
    initializeArrays();
    loadPostModern();
    computeAverageImage();
  }
  // btn 4
  if (mouseY>777 && mouseY<793 && mouseX>515 && mouseX<529) {    
    initializeArrays();
    loadColorComposite();
    computeAverageImage();
  }
// bonus zoom
  if (mouseY>500 && mouseY<650 && mouseX>290 && mouseX<400) {  
    initializeArrays();
    loadZoom();
    computeAverageImage();
  }
}
 
/////////////////////////////////////////////////////////
////////             PIXEL LOADING               ////////
/////////////////////////////////////////////////////////
 
void loadAll() {
  // Loads and counts image files in data folder
  String dataFolderPath = dataPath("");
  File myDataFolder = new File(dataFolderPath);
 
  String[] imageFilenames = myDataFolder.list();
  if (imageFilenames != null) {
    nImages = imageFilenames.length;
    images = new PImage[nImages];
 
    int count = 0; 
    for (int i=0; i<nImages; i++) {
      String filename = imageFilenames[i];
      if (filename.startsWith("g")) {
        images[count] = loadImage(filename);
        count++;
      }
    }
    nImages = count;
  }
}
 
void loadOld() {
  // Loads and counts image files in data folder
  String dataFolderPath = dataPath("");
  File myDataFolder = new File(dataFolderPath);
 
  String[] imageFilenames = myDataFolder.list();
  if (imageFilenames != null) {
    nImages = imageFilenames.length;
    images = new PImage[nImages];
 
    int count = 0; 
    for (int i=0; i<nImages; i++) {
      String filename = imageFilenames[i];
      if (filename.startsWith("g0old") || filename.startsWith("g1old") || filename.startsWith("g2old")) {
        images[count] = loadImage(filename);
        count++;
      }
    }
    nImages = count;
  }
}
 
void loadModern() {
  // Loads and counts image files in data folder
  String dataFolderPath = dataPath("");
  File myDataFolder = new File(dataFolderPath);
 
  String[] imageFilenames = myDataFolder.list();
  if (imageFilenames != null) {
    nImages = imageFilenames.length;
    images = new PImage[nImages];
 
    int count = 0; 
    for (int i=0; i<nImages; i++) {
      String filename = imageFilenames[i];
      if (filename.endsWith("picasso.png")) {
        images[count] = loadImage(filename);
        count++;
      }
    }
    nImages = count;
    loadPixels();
  }
}
 
void loadPostModern() {
  // Loads and counts image files in data folder
  String dataFolderPath = dataPath("");
  File myDataFolder = new File(dataFolderPath);
 
  String[] imageFilenames = myDataFolder.list();
  if (imageFilenames != null) {
    nImages = imageFilenames.length;
    images = new PImage[nImages];
 
    int count = 0; 
    for (int i=0; i<nImages; i++) {
      String filename = imageFilenames[i];
      if (filename.endsWith("pm.png")) {
        images[count] = loadImage(filename);
        count++;
      }
    }
    nImages = count;
  }
}
 
void loadZoom() {
  // Loads and counts image files in data folder
  String dataFolderPath = dataPath("");
  File myDataFolder = new File(dataFolderPath);
 
  String[] imageFilenames = myDataFolder.list();
  if (imageFilenames != null) {
    nImages = imageFilenames.length;
    images = new PImage[nImages];
 
    int count = 0; 
    for (int i=0; i<nImages; i++) {
      String filename = imageFilenames[i];
      if (filename.startsWith("Maria")) {
        images[count] = loadImage(filename);
        count++;
      }
    }
    nImages = count;
  }
}
 
/////////////////////////////////////////////////////////
////////              SALVONIZING                ////////
/////////////////////////////////////////////////////////
// from Jason Salvon
 
void  initializeArrays() {
  // make and clear the average image
  averageImagef = new float[nPixels][4]; 
  averagedPixelColors = new color[nPixels];
  for (int i=0; i<nPixels; i++) {
    averageImagef[i][0] = 0;
    averageImagef[i][1] = 0;
    averageImagef[i][2] = 0;
    averageImagef[i][3] = 0;
  }
}
 
void loadColorComposite() {
  // Loads and counts image files in data folder
  String dataFolderPath = dataPath("");
  File myDataFolder = new File(dataFolderPath);
 
  String[] imageFilenames = myDataFolder.list();
  if (imageFilenames != null) {
    nImages = imageFilenames.length;
    images = new PImage[nImages];
 
    int count = 0; 
    for (int i=0; i<nImages; i++) {
      String filename = imageFilenames[i];
      if (filename.startsWith("cc")) {
        images[count] = loadImage(filename);
        count++;
      }
    }
    nImages = count;
  }
}  
 
void computeAverageImage() {
  if (nImages > 0) {
 
    for (int i=0; i<nPixels; i++) {              // for each pixel
      for (int j=0; j<nImages; j++) {            // for each image
        color c = images[j].pixels[i];          // get the color of that pixel in that image
        float r = red   (c);                    // extract the color components of that pixel
        float g = green (c);
        float b = blue  (c);
        float a = alpha (c);
        averageImagef[i][0] += r;               // sum (accumulate) the color components
        averageImagef[i][1] += g;
        averageImagef[i][2] += b;
        averageImagef[i][3] += a;
      }
    }
 
    for (int i=0; i<nPixels; i++) {
 
      // divide the sums by the number of images, to get averages
      averageImagef[i][0] /= (float)nImages;
      averageImagef[i][1] /= (float)nImages;
      averageImagef[i][2] /= (float)nImages;
 
      // create and store new colors from these averages
      float r = averageImagef[i][0];
      float g = averageImagef[i][1];
      float b = averageImagef[i][2];
      averagedPixelColors[i] = color (r,g,b);
    }
  }
}
 
 
/////////////////////////////////////////////////////////
////////                 TEXT                    ////////
/////////////////////////////////////////////////////////
 
void displayText() {
  // Texts
  textFont(palatinoB);
  textSize(24);
  fill(165,165,165,127);
  text("Remembering",20,30);
  textFont(palatinoI);
  textSize(24);
  text("Velázquez",175,30);
  textFont(palatinoBI);
  textSize(16);
  // Load button names into a list
  ArrayList<String> btn = new ArrayList<String>(btnCount);
  btn.add(" Composite");
  btn.add(" Baroque");
  btn.add(" Modern");
  btn.add(" P.Modern");
  //  btn.add(" Portraits");
  btn.add(" Color Composite");
  // Distribute names evenly next to buttons
  for (int i=0; i<5; i++) {
    int x = ((int)(width * (float)(i+1)/(btnCount+1)))-45;
    int y = height-9;
    //fill(0,0,0,50);
    //////    drop Shadow   //////
    fill(255,255,255,100);
    //text(btn.get(i),x+1,y+1);
    //text(btn.get(i),x-1,y+1);
    text(btn.get(i),x-1,y-1);
    text(btn.get(i),x+1,y-1);
    fill(0,0,0,100);
    text(btn.get(i),x,y);
    //////////////////////////////
 
  }
}
 
// from Processing: A Programming Handbook for Visual Designers
 
class Radio {
  int x, y;                 // The x- and y-coordinates of the rect
  int size, dotSize;        // Dimension of circle, inner circle
  color baseGray, dotGray;  // Circle gray value, inner gray value
  boolean checked = false;  // True when the button is selected
  int me;                   // ID number for this Radio object
  Radio[] others;           // Array of all other Radio objects
 
  Radio(int xp, int yp, int s, color b, color d, int m, Radio[] o) {
    x = xp;
    y = yp;
    size = s;
    dotSize = size - size / 3;
    ;
    baseGray = b;
    dotGray = d;
    others = o;
    me = m;
  }
 
  // Updates the boolean value press, returns true or false
  boolean press(float mx, float my) {
    if (dist(x, y, mx, my) < size / 2) {
      checked = true;
      for (int i = 0; i < others.length; i++) {
        if (i != me) {
          others[i].checked = false;
        }
      }
      return true;
    } else {
      return false;
    }
  }
 
  // Draws the element to the display window
  void display() {
    noStroke();
    fill(baseGray);
    ellipse(x, y, size, size);
    if (checked == true) {
      fill(dotGray);
      ellipse(x, y, dotSize, dotSize);
    }
  }
}

3 Comments

  1. Interesting analysis of a large host of work. Could you use data to produce your own version?

    Are all the pieces the same height/width ratio? How did you make sure that the images lined up properly?

    Comment by James Mulholland — 26 January 2011 @ 9:20 am
  2. Comments from PiratePad A:

    Nice presentation! Your viz was very interesting.

    Nice presentation. What’s this software?
    http://prezi.com/ apparently

    Ambitious.

    Interesting how abstracting away the details of the individual paintings allows you to see the similarities of the set as a whole.

    I appreciate your typography. (agreed!) What’s that font?
    Oh, Palatino. Well applied.

    Fantastic. When I first heard your concept I was like I’m not really sure how you can data viz this thing. However I think what you have achieved is truly fascinating and really reveal insights to the interpretation of this painting as it changes over time. Good job!

    great keynote or whatever that software was. I like how you subsetted by style of art, although the composite seems to be interesting as well.

    Great process presentation. Nice navigation for the actual visualization, perhaps a small amount of the explanation could be on the first screen of the processing program.

    I really like this painting; I’m excited to see what you found.
    How did you get a consistent positioning for all the pictures? I noticed a lot of the replicas focus on only one part of the painting, and I assume they were all varying sizes.

    Really interesting insight into something that seems so two dimensional. It adds a lot of information to what was once just an artistic representation.

    More interesting and useful than most image averaging projects

    The way you worked to line up all the different paintings in photoshop worked pretty well. I would love to have seen you walk in with a few high quality huge plots to really show it off.ff. I feel there is a lot of small data sitting at the bottom of those images, and to see it in a large scale or maybe even being able to zoom in in processing would be really effective. Myabe a “debug” mode where rectangles are drawn around individual images, so that we could have a more analytical understanding of how the paintings relate before we see their relationships through the averaging. Try plotting them.

    The interface is very usable. Maybe some tooltips would help onlookers who are less familar with conventions / practices during each art period?

    I really like your concept. It is interesting to see the different paintings with the “x-ray” filter. This definitely gives a new way of looking at this painting and seems to work well for your visualization. I also like how you broke the averages down into different categories to compare. Nice presentation as well!

    Bummer with the resolution. I like the visualization, you can see the differences between the images. It would be nice if you could maybe map the magnitude of change per pixel to a color value, so the resulting image would illuminate portions of greater change with a color as well. Also, maybe you can compare differences in the images using heuristics instead of commparing pixel values.

    Comment by Golan Levin — 26 January 2011 @ 3:05 pm
  3. Comments from PiratePad B:

    What meaning is actually derived from looking at the “baroque” XRay version of the painting? By compositing other people’s interpretations on top of the original, you can see what was carried from the original composition, but with loose interpretations such as Picassos, I’m not sure meaningful an overlay is.

    I think the color composite is the most intersting version. Agreed. I think more could be done with that, tracking the differences in colors between iterations.

    I think it’s almost more interesting that the painting has been interpreted in so many styles and ways, rather than the composition staying the same. Yes the paintings are different, but once you image that together you can’t really see that…although I suppose that’s the point. :)

    I liked how the “X-Ray composite” looked.
    Were the images lined up manually? Some photos are details and others are the whole painting.

    I’d like it if the composite could retain some more aspects of the various originals. I agree with the above, you lose some of the most interesting parts of your data.

    I liked how the composite is like your own addition to this collection of various artists’ intrepretations of the painting.

    Idea was cool, the presentation probably could have done it more justice with a little more energy.

    I’m thinking that the “small multiples” visualization (from Edward Tufte) allows the differences to be better seen, while the image averaging approach eliminates the differences and only shows the commonalities. The question is: in what ways are the commonalities thus revealed, interesting or surprising?

    Comment by Golan Levin — 26 January 2011 @ 3:07 pm

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.
(c) 2019 Interactive Art & Computational Design / Spring 2011 | powered by WordPress with Barecity