• fullscreen
  • MosaicScreen.pde
  • SelectScreen.pde
  • flickrGet.pde
  • imageIcon.pde
  • makeMosaic.pde
  • mosaic.pde
  • boolean drawMosaic = false;
    
    void drawMosaicScreen() {
      drawMainImage(255);
      
      if (!mosaicComplete) {
        makeMosaic();
        mosaicComplete = true;
      } else {
        if (drawMosaic) {
          drawMosaic();
          
          if (useOverlay) {
            drawMainImage(70);
          }
        }
      }
    }
    
    void drawMainImage(int opacity) {
      imageMode(CENTER);
      tint(255, opacity);
      image(mainImage, width/2, height/2, mosaicW, mosaicH);
      tint(255, 255); // reset tint
    }
    
    void drawMosaic() {
      for (int row=0; row<numRow; row++) {
        for (int col=0; col<numCol; col++) {
          PVector loc = getTilePos(row,col);
          int tileRef = mosaicRef[row][col];
          image(imgTiles[tileRef], loc.x+marginLR, loc.y+marginTB, tileW, tileH);
        }
      }
    }
    
    
    void mosaicScreenClick() {
      drawMosaic = !drawMosaic;
    }
    
    void backToSelectScreen () {
      // if user presses b, return to select screen
      mosaicScreen = false;
      selectScreen = true;
      mosaicComplete = false;
      drawMosaic = false;
    }
    
    
    
    void setupSelectScreen() {
      
      float margin = 20;
      float tileW = (width-margin*(numSelectCol+1)) / numSelectCol;
      float tileH = (height-margin*(numSelectRow+1)) / numSelectRow;
      
      int imgRef = imgPointer;
      for (float i=0; i<numSelectRow; i++) { // i = row
        for (float j=0; j<numSelectRow; j++) { // j = col
          float xLoc, yLoc;
          if (j==0) {
            xLoc = margin + tileW/2;
          } else {
            xLoc = margin + tileW/2 + j*(margin + tileW);
          }
          if (i==0) {
            yLoc = margin + tileH/2;
          } else {
            yLoc = margin + tileH/2 + i*(margin + tileH);
          }
          
          PImage curImage;
          boolean refresher;
          if (i==1 && j==1) {
            curImage = loadImage("refresh.jpg");
            refresher = true;
          } else {
            curImage = imgPool[imgRef].get();
            refresher = false;
            imgRef++;
            if (imgRef >= poolSize) {
              imgRef = 0;
            }
          }
          selectTiles[int(j+i*numSelectRow)] = new ImageIcon(curImage, xLoc, yLoc, tileW, tileH, refresher); 
        }
      }
      
    }
    
    
    
    void drawSelectScreen() {
      background(0);
      
      for (int i=0; i<numSelectTiles; i++) {
        selectTiles[i].drawIcon();
      }
      
    }
    
    
    
    void selectScreenClick() {
      // run when a click is made on the select screen
      
      for (int i=0; i<numSelectTiles; i++) {
        if (selectTiles[i].hovered) {
          if (selectTiles[i].refresher) {
            println("refreshed");
            refreshImages();
          } else {
            // select image as main image and begin mosaic-ing
            PImage tempImg = selectTiles[i].img;
            loadMainImage(tempImg.get());
            selectScreen = false;
            mosaicScreen = true;
          }
        }
      }
    }
    
    
    void refreshImages() {
      // queries 8 photos from flickr and draws tiles
      imgPointer += 8;
      if (imgPointer >= poolSize) {
              imgPointer = imgPointer - poolSize - 1;
      }
      
      int imgRef = imgPointer;
      for (int i=0; i<numSelectRow; i++) { // i = row
        for (int j=0; j<numSelectRow; j++) { // j = col
          if (!(i==1 && j==1)) {
            // replace image
            if (imgRef >= poolSize) {
              imgRef = 0;
            }
            PImage curImage = imgPool[imgRef].get();
            selectTiles[j+i*numSelectRow].img = curImage;
            imgRef++;
            
          }
        }
      }
    }
    
    // used to grab images from flickr
    void flickrGet(String searchTerm, int numImages) {
      println("Getting images from flickr...");
      try {
        searchTerm = URLEncoder.encode(searchTerm, "UTF-8");
      } catch (Exception e) {
        e.printStackTrace();
        exit();
        return;
      }
      String url =  "http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=bfaa37fbd00c1291ced65b903fb15fda&text=" 
                      + searchTerm + "&format=rest";
      XMLElement xml = new XMLElement(this, url);
      xml = xml.getChild(0);
      XMLElement[] photos = xml.getChildren();
      int maximum = min(numImages, photos.length - 1);
      for(int i=0; i < maximum; i++) {
        XMLElement photo = photos[i];
        String imgURL = "http://farm" + photo.getStringAttribute("farm") 
                  + ".static.flickr.com/" 
                  + photo.getStringAttribute("server") + "/" 
                  + photo.getStringAttribute("id") + "_" 
                  + photo.getStringAttribute("secret") + "_z.jpg";
                  
        println(imgURL);
                  
        imgPool[i] = loadImage(imgURL);
        println("downloading " + (i+1) + "/" + maximum);
      }
      
      poolSize = maximum;
    }
    
    
    // used to populate images from file (not flickr)
    void loadImages() {
      for (int i=0; i<poolSize; i++) {
        String filename = "img" + i + ".jpg";
        
        imgPool[i] = loadImage(filename);
        if (imgPool[i] == null) {
          poolSize = i;
          break;
        }
          
      }
    }
    
    class ImageIcon {
      PImage img;
      float h, w, origH, origW;
      float xloc, yloc;
      boolean hovered;
      boolean refresher;
      
      
      ImageIcon(PImage img_, float xloc_, float yloc_, float w_, float h_, boolean refresher_) {
        img = img_.get();
        xloc = xloc_;
        yloc = yloc_;
        w = w_;
        h = h_;
        refresher = refresher_;
        
        hovered = false;
        origW = w;
        origH = h;
      }
      
      
      void drawIcon() {
        checkHover();
        
        // draw image
        imageMode(CENTER);
        image(img, xloc, yloc, w, h);
          
      }
      
      void checkHover() {
        
        hovered = (mouseX > xloc-w/2 && mouseX < xloc+w/2) && (mouseY > yloc-h/2 && mouseY < yloc+h/2);
        
        if (hovered) {
          w = origW * 1.2;
          h = origH * 1.2;
        } else {
          w = origW;
          h = origH;
        }
        
      }
      
    }
    
    void makeMosaic() {
      // function: creates mosaic of main image from available tiles
      
      // create smaller tiles of image pool
      createTiles();
      
      // compute average color for each tile, for each quadrant separately
      color[] avgTileColor0 = new color[poolSize];
      color[] avgTileColor1 = new color[poolSize];
      color[] avgTileColor2 = new color[poolSize];
      color[] avgTileColor3 = new color[poolSize];
      PImage tileQuad0,tileQuad1,tileQuad2,tileQuad3;
      for (int i=0; i<poolSize; i++ ){
        tileQuad0 = imgTiles[i].get(0,0,int(tileW/2),int(tileH/2));
        tileQuad1 = imgTiles[i].get(int(tileW/2-1),0,int(tileW/2),int(tileH/2));
        tileQuad2 = imgTiles[i].get(0,int(tileH/2-1),int(tileW/2),int(tileH/2));
        tileQuad3 = imgTiles[i].get(int(tileW/2-1),int(tileH/2-1),int(tileW/2),int(tileH/2));
        
        avgTileColor0[i] = averageColorRGB(tileQuad0);
        avgTileColor1[i] = averageColorRGB(tileQuad1);
        avgTileColor2[i] = averageColorRGB(tileQuad2);
        avgTileColor3[i] = averageColorRGB(tileQuad3);
      }
      
      for (int row=0; row<numRow; row++) {
        for (int col=0; col<numCol; col++) {
          
          // get original section from main image to be replaced by a tile
          PVector tilePos = getTilePos(row,col);
          PImage origSection = mainImage.get(int(tilePos.x-tileW/2), int(tilePos.y-tileH/2), int(tileW), int(tileH));
          
          //****search through images to find best match****
          // find top 3 choices (so images dont directly border each other)
          int rankingLength = minDist*minDist + (minDist-1)*(minDist-1);
          int[] bestMatch = new int[rankingLength];
          for (int i=0; i<rankingLength; i++) { bestMatch[i] = i; }
          float[] bestVariance = new float[rankingLength];
          for (int i=0; i<rankingLength; i++) { bestVariance[i] = 999999999; }
          
          for (int p=0; p<poolSize; p++) { // loop thru image tile pool
            
            color avgOrig, avgTile;
            float variance = 0.0;
            
            // split images into 4 quadrants and separately calculate color variance
            for (int quadrant=0; quadrant<4; quadrant++) {
              PImage origQuad;
              switch (quadrant) {
                case 0:
                  origQuad = origSection.get(0,0,int(tileW/2),int(tileH/2));
                  avgTile = avgTileColor0[p];
                  break;
                case 1:
                  origQuad = origSection.get(int(tileW/2-1),0,int(tileW/2),int(tileH/2));
                  avgTile = avgTileColor1[p];
                  break;
                case 2:
                  origQuad = origSection.get(0,int(tileH/2-1),int(tileW/2),int(tileH/2));
                  avgTile = avgTileColor2[p];
                  break;
                default:
                  origQuad = origSection.get(int(tileW/2-1),int(tileH/2-1),int(tileW/2),int(tileH/2));
                  avgTile = avgTileColor3[p];
                  break;
              }
              
            
              avgOrig = averageColorRGB(origQuad);
              
              variance += getColorDifRGB(avgOrig, avgTile);
             
              
            } // end loop thru quadrants
            
            for (int i=0; i<rankingLength; i++) {
              if (variance < bestVariance[i]) {
                // shift down and insert here
                for (int j=rankingLength-1; j>i; j-- ) {
                  bestMatch[j] = bestMatch[j-1];
                  bestVariance[j] = bestVariance[j-1];
                }
                bestMatch[i] = p;
                bestVariance[i] = variance;
                break;
              }
            }
            
    
          } // end loop thru image pool
          
          // dont let same tile be used next to each other
          boolean[] dontUse = new boolean[rankingLength];
          for (int i=0; i<rankingLength; i++ ) { dontUse[i] = false; }
          
          int colsLeft, rowsAbove, colsRight;
          colsLeft = min(col, minDist-1);
          rowsAbove = min(row, minDist-1);
          colsRight = min(numCol-1, col+minDist-1);
          for (int i = col-colsLeft; i <= colsRight; i++ ) {
            for (int j = row-rowsAbove; j <= row; j++ ) {
              for (int du=0; du < rankingLength; du++) {
                if (i != col || j != row) {
                  dontUse[du] = dontUse[du] || (mosaicRef[j][i] == bestMatch[du]);
                }
              }
            }
          }
    
          
          // select best match tile
          mosaicRef[row][col] = bestMatch[rankingLength-1];
          for (int du=0; du < rankingLength; du++) {
            if (!dontUse[du]) {
              // we found a usable tile
              mosaicRef[row][col] = bestMatch[du];
              break;
            }
          }
          
          
        } // loop thru columns
      } // loop thru rows
      
    } // end function
    
    
    
    void createTiles() {
      // function: make smaller versions of the image pool with same aspect ratio
      // as the mainImage
      
      numCol = int(round(mosaicW / maxTileSize)) + 1;
      numRow = int(round(mosaicH / maxTileSize)) + 1;
      tileW = mosaicW / numCol;
      tileH = mosaicH / numRow;
      mosaicRef = new int[numRow][numCol];
      
      for (int i=0; i<poolSize; i++) {
        imgTiles[i] = imgPool[i].get();
        imgTiles[i].resize(int(tileW), int(tileH));
      }
      
    }
    
    
    
    PVector getTilePos(int row, int col) {
      // returns location of center of tile in drawing screen
      
      PVector answer = new PVector(0.0,0.0);
      answer.x = tileW/2+col*tileW;
      answer.y = tileH/2+row*tileH;
      return answer;
    }
    
    
    void loadMainImage(PImage img) {
      mainImage = img.get();
      if (mainImage.width > mainImage.height) {
        marginTB = (float(height) - float(mainImage.height)*(float(width)/float(mainImage.width))) / 2;
        marginLR = 0;
        mosaicH = float(mainImage.height)*(float(width)/float(mainImage.width));
        mosaicW = width;
      } else {
        marginTB = 0;
        marginLR = (float(width) - float(mainImage.width)*(float(height)/float(mainImage.height))) / 2;
        mosaicW = float(mainImage.width)*(float(height)/float(mainImage.height));
        mosaicH = height;
      }
      mainImage.resize(int(mosaicW), int(mosaicH));
    }
    
    
    
    color averageColorRGB(PImage img) {
      colorMode(RGB, 255);
      float avgH=0, avgS=0, avgB=0;
      float totalPix = (img.width)*(img.height);
      for (int j=0; j<img.width; j++) {
        for (int i=0; i<img.height; i++) {
          color pixel = img.get(j,i);
          avgH += red(pixel) / totalPix;
          avgS += green(pixel) / totalPix;
          avgB += blue(pixel) / totalPix;
        }
      }
      return color(avgH, avgS, avgB);
    }
    
    
    float getColorDifRGB(color c1, color c2) {
      float variance;
      colorMode(RGB, 255);
      variance = pow(red(c1) - red(c2), 2) + pow(green(c1) - green(c2), 2) + pow(blue(c1) - blue(c2), 2);
      return variance;
    }
    
    //------MODIFIERS-------//
    String query = "flower,canon";
    boolean grabFromFlickr = false;  // if false, will instead grab images from file (of names 'img0.jpg', 'img1.jpg' etc)
    boolean useOverlay = true;  // "cheat" by displaying transparent original image over mosaic
    int maxTileSize = 15;  // max height/width in pixels of mosaic tiles
    int poolSize = 99;  // number of images to download from flickr (max 99)
    int minDist = 4;  // minimum distance between like tiles (max 6 for pool of 99)
    //----------------------//
    
    import processing.opengl.*;
    import java.net.URLEncoder;
    
    int numSelectTiles = 9, numSelectRow = 3, numSelectCol = 3;
    ImageIcon[] selectTiles = new ImageIcon[numSelectTiles];
    boolean selectScreen = true, mosaicScreen = false, mosaicComplete = false;
    boolean mouseClick = false;
    
    PImage mainImage;
    PImage[] imgPool = new PImage[poolSize];
    PImage[] imgTiles = new PImage[poolSize];
    float tileW, tileH;
    int numRow, numCol;
    float mosaicW, mosaicH;
    float marginLR, marginTB;
    int[][] mosaicRef;
    int imgPointer = 0;
    
    void setup() {
      size(600,600,OPENGL);
      smooth();
      
      // get images from flickr
      if (grabFromFlickr) {
        flickrGet(query, poolSize); // from flickr
      } else {
        loadImages(); // from file
      }
      
      // setup select screen
      setupSelectScreen();
    }
    
    
    void draw() {
      background(0);
      
      if (selectScreen) {
        drawSelectScreen();
      } else if (mosaicScreen) {
        drawMosaicScreen();
      }
      
    }
    
    void mouseClicked() {
      if (!mouseClick) {
        mouseClick = true;
        
        if (selectScreen) {
          selectScreenClick();
        } else if (mosaicScreen) {
          mosaicScreenClick();
        }
        
      }
    }
    
    void mouseReleased() {
      mouseClick = false;
    }
    
    void keyPressed() {
      if (mosaicScreen && (key == 'b' || key == 'B')) {
        // return to image select screen
        backToSelectScreen();
      } else if (mosaicScreen && (key == 's' || key == 'S')) {
        // save frame to file
        println("Image saved");
        saveFrame();
      }
      
    }
    

    code

    tweaks (0)

    about this sketch

    This sketch is running as Java applet, exported from Processing.

    license

    advertisement

    Joe Kurleto

    Image Mosaic

    Add to Faves Me Likey@! 1
    You must login/register to add this sketch to your favorites.

    This program implements an algorithm for creating image mosaics - creating one image by mosaicing many images together. There are some in-code parameters that can be changed (sorry, no GUI yet). One of these parameters let's you grab images from flickr, but it defaults to using a bunch of giraffe images I downloaded. The first scene lets you select an image to create a mosaic of. Once you have selected it and you see the image, click to toggle showing the mosaic or the original.

    Joe Kurleto
    29 Sep 2011
    PROBLEM: Not sure why yet, but the applet version doesn't want to work. Maybe it has to do with loading ~100 images that I had in the data folder? Help!
    You need to login/register to comment.