morph images effect in Processing

481 Views Asked by At

I would like to build a kind of image morphing tool in Processing. Similar to what you can see in this link:

https://giphy.com/gifs/painting-morph-oil-c8ygOpL64UDuw

My first step to achieve this was to build a two-dimensional grid of pixels. The pixels are filled with colour. The fill colour is created by reading colour from an image (PImage img1;) with the get(); function. This is how I recreated an image with my pixels. In the second step, I thought I would use the lerp(); function to give the respective pixels the colour of a second image (PImage img2;) - I thought this would create the desired morph effect. But I was wrong! The whole thing works - but the effect is only that a fade-in takes place between the two images. And no morphing. What exactly happens to pixels while this morph effect? How could I recreate it in Processing?

float pixel;
float pixelsize;
PImage img1;
PImage img2;
float counter;

void setup() {
  size(1080, 1080);
  pixel = 100;
  pixelsize = width/pixel;
  noStroke();
  img1 = loadImage("0.jpg");
  img2 = loadImage("1.jpg");
  counter = 0;
}


void draw() {
  background(255);

  for (int y = 0; y < pixel; y++) {
    for (int x = 0; x < pixel; x++) {
      color c1 = img1.get(int(pixelsize*x), int(pixelsize*y));
      color c2 = img2.get(int(pixelsize*x), int(pixelsize*y));

      color from = c1;
      color to = c2;
      color interA = lerpColor(from, to, counter);

      pushMatrix();
      translate(pixelsize*x, pixelsize*y);
      fill(interA);
      rect(0, 0, pixelsize, pixelsize);
      popMatrix();
    }
  }
  counter= counter + 0.01;
}
1

There are 1 best solutions below

5
George Profenza On

Indeed it is not a straight forward task.

You're approach is not a bad start: it would result in a nice crossfade between the two images.

Bare in mind get() can be costly on the CPU. You can however use the pixels[]:

PImage img1;
PImage img2;
// transition image
PImage imgT;

void setup() {
  size(1080, 1080);
  
  img1 = loadImage("0.jpg");
  img2 = loadImage("1.jpg");
  // copy the 1st image (copies width/height as well)
  imgT = img1.get();
}


void draw() {
  background(255);
  // map transition amount to mouse X position
  float t = map(mouseX, 0, width, 0.0, 1.0);
  
  // make all pixels readable
  imgT.loadPixels();
  // lerp each pixel
  for(int i = 0 ; i < imgT.pixels.length; i++){
    imgT.pixels[i] = lerpColor(img1.pixels[i], img2.pixels[i], t);
  }
  // update all pixels in one go
  imgT.updatePixels();
  
  // display result
  image(imgT, 0, 0);
}

Implementing a full morph image is non-trivial. I can recomend two options to make use of existing algorithms, however these options are also not beginner friendly:

  1. ImageMagick implements shepards distortion and there is a java library that interfaces with imagemagick: im4java. Note that you'd need to download the precompiled java library and drop the .jar file on top of your sketch and processing the output might take time: probably not feasible for realtime (however it should be possible to save individual frames to disk and assemble them as a gif/movie/etc.)
  2. Using OpenCV: there's an OpenCV Face Morph tutorial with source code in c++ or Python and there is a Processing OpenCV library. It would be a matter of porting the c++/Python OpenCV calls to the Java OpenCV API.