Face Morphing

Views:8794Difficulty: 4 Status: Complete
Fm_w_hand

Mixing pixels from two face images together.

Use the Joy Gamer to mix face images together!We can mix two images using a weighted average in each color channel (red, green, blue) of each pixel in the images. Moving the joystick shifts the weight between the two images. So, if the joystick is all the way to the right then the right image dominates, if the joystick is in the middle the two images have equal weight and when the joystick is all the way left the left image's pixels are weighted 1.0 and the right image's pixels are weighted 0.0. This means we will only see the left image. If the joystick is untouched and resting in the cente then the wieghts will 0.5 and 0.5 and image displayed will be an even mix of the two pictures. These joysticks have a push button included. When that button is pushed the image on that side switches. The screen is adafruit's 1.8" TFT LCD Screen .

Barack and Sarah

Screen_shot_2013-02-22_at_10.39.37_am
Sarah Michelle Geller's Eyes mixed with the prez.

Face Morphing Video

Face Morphing in action!

Time Lapsy Video

Watch the faces get morphed in fast forward!

Arduino Code

This whole project basically boils down to these three lines of code:
      float avg_b = ((float)b*ratio )+ ((float)b2*(1-ratio));      float avg_g = ((float)g*ratio )+ ((float)g2*(1-ratio));      float avg_r = ((float)p*ratio )+ ((float)p2*(1-ratio));
the floating point variable 'ratio' controls the weight of each pixel. Notice that ratio is always between 0 and 1. So if ratio is 0.3 then 1-ratio = 0.7 and we will take 70% of the second pixel and 30% of the first. The code below is for the Joy Gamer so you will need to download the necessary libraries available at our basic Joy Gamer tutorial .
Download File
/* LucidTronix Joy Gamer SD Navigator * If the compiler gives you an error about sketch being too big make sure to * COMMENT OUT both the USE_ACCELEROMETER and the USE COLOR defines in the JoyGamer.h file.   * They should look like this: //#define USE_ACCELEROMETER //#define USE_COLOR * For more instructions, details and schematic, See: * http://www.lucidtronix.com/tutorials/27 */#include #include     // Core graphics library#include  // Hardware-specific library#include #include Adafruit_ST7735  tft = Adafruit_ST7735(SS, 9, 8);JoyGamer jg = JoyGamer(&tft);File bmpFile1;File bmpFile2;// information we extract about the bitmap fileint bmpWidth, bmpHeight;int bmpWidth2, bmpHeight2;uint8_t bmpDepth, bmpImageoffset;uint8_t bmpDepth2, bmpImageoffset2;float ratio = 0.5;int img_index1 = 12;int img_index2 = 16;int num_images = 60;void setup(void) {  jg.initialize();  jg.start_sd_card();  bmp_load_and_draw_2_images("face36.bmp","face34.bmp");}void loop() {  bmpdraw_2_images();}void bmp_load_and_draw_2_images(char* filename1, char* filename2) {  bmpFile1 = SD.open(filename1);  bmpFile2 = SD.open(filename2);  if (!bmpFile1) {    jg.draw_string(12,62, "Coodn't find image", ST7735_RED);  }  if (!bmpReadHeader(bmpFile1,0)) {     jg.draw_string(12,82, "Bad image" , ST7735_RED);  }  if (!bmpFile2) {    jg.draw_string(12,62, "Coodn't find image 2" , ST7735_RED);  }  if (!bmpReadHeader(bmpFile2,1)) {     jg.draw_string(12,82, "Bad image 2" , ST7735_RED);  }  bmpdraw_2_images();}/*********************************************/// This procedure reads 2 bitmaps and draws an average to the screen// its sped up by reading many pixels worth of data at a time// instead of just one pixel at a time. increading the buffer takes// more RAM but makes the drawing a little faster. 20 pixels' worth// is probably a good place#define BUFFPIXEL 20void bmpdraw_2_images() {  bmpFile1.seek(bmpImageoffset);  bmpFile2.seek(bmpImageoffset2);  uint32_t time = millis();  uint16_t p, p2;   uint8_t g, b;  uint8_t g2, b2;  int i, j;  unsigned int file_pos = bmpImageoffset;  uint8_t sdbuffer[3 * BUFFPIXEL];  // 3 * pixels to buffer  uint8_t buffidx = 3*BUFFPIXEL;  uint8_t sdbuffer2[3 * BUFFPIXEL];  // 3 * pixels to buffer  //set up the 'display window'  tft.setAddrWindow(0, 0, bmpWidth-1, bmpHeight-1);  for (i=0; i< bmpHeight; i++) {    // bitmaps are stored with the BOTTOM line first so we have to move 'up'    for (j=0; j= 3*BUFFPIXEL) {        bmpFile1.read(sdbuffer, 3*BUFFPIXEL);        bmpFile2.read(sdbuffer2, 3*BUFFPIXEL);        buffidx = 0;      }      // convert pixel from 888 to 565      b = sdbuffer[buffidx++];     // blue      g = sdbuffer[buffidx++];     // green      p = sdbuffer[buffidx++];     // red      // convert pixel from 888 to 565      buffidx -= 3;        b2 = sdbuffer2[buffidx++];     // blue      g2 = sdbuffer2[buffidx++];     // green      p2 = sdbuffer2[buffidx++];     // red      file_pos += 3;      float avg_b = ((float)b*ratio )+ ((float)b2*(1-ratio));      float avg_g = ((float)g*ratio )+ ((float)g2*(1-ratio));      float avg_r = ((float)p*ratio )+ ((float)p2*(1-ratio));      b = (int) avg_r;      g = (int) avg_g;      p = (int) avg_b;      p >>= 3;      p <<= 6;      g >>= 2;      p |= g;      p <<= 5;      b >>= 3;      p |= b;      tft.drawPixel(j, bmpHeight - i, p);    }    if( digitalRead(jg.joystick_btn_pin) == HIGH){      reset_image(&img_index1, &bmpFile1, file_pos);     }    if(digitalRead(jg.btn_pin) == LOW) {      reset_image(&img_index2, &bmpFile2, file_pos);     }    float cur_val = analogRead(0);    ratio = cur_val / 1024.0;  }}void reset_image(int *index, File *a_file, int file_pos){  *index = ++*index % num_images;  a_file->close();  String img2 = "face";  img2 += String(*index);  img2 += ".bmp";  char  img2c[11] ;  for(int i = 0; i < 10; i++){    img2c[i] = img2.charAt(i);  }  if (*index < 10 ) {    for(int i = 10; i >= 5; i--){      img2c[i] = img2c[i-1];    }    img2c[4] = '0';  }  img2c[10] = 0;  *a_file = SD.open(img2c);  a_file->seek(file_pos); }boolean bmpReadHeader(File f, int mode) {  // read header  uint32_t tmp;  if (read16(f) != 0x4D42) {    // magic bytes missing    return false;  }  // read file size  tmp = read32(f);    //Serial.print("size 0x"); Serial.println(tmp, HEX);  // read and ignore creator bytes  read32(f);  if (mode == 0 ) bmpImageoffset = read32(f);   else if (mode == 1 ) bmpImageoffset2 = read32(f);    //Serial.print("offset "); Serial.println(bmpImageoffset, DEC);  // read DIB header  tmp = read32(f);  //Serial.print("header size "); Serial.println(tmp, DEC);  if (mode == 0 ) {    bmpWidth = read32(f);    bmpHeight = read32(f);  }   else if (mode == 1 ) {    bmpWidth2 = read32(f);    bmpHeight2 = read32(f);  }  if (read16(f) != 1)    return false;  if (mode == 0 ) bmpDepth = read16(f);  else if (mode == 1 ) bmpDepth2 = read16(f);  //Serial.print("bitdepth "); Serial.println(bmpDepth, DEC);  if (read32(f) != 0) {    // compression not supported!    return false;  }  return true;}/*********************************************/// These read data from the SD card file and convert them to big endian // (the data is stored in little endian format!)// LITTLE ENDIAN!uint16_t read16(File f) {  uint16_t d;  uint8_t b;  b = f.read();  d = f.read();  d <<= 8;  d |= b;  return d;}// LITTLE ENDIAN!uint32_t read32(File f) {  uint32_t d;  uint16_t b;  b = read16(f);  d = read16(f);  d <<= 16;  d |= b;  return d;}

Parts

Title Description # Cost Link Picture
Joy Gamer Joyous Arduino-Based Gaming machine! Full color LCD screen, Joystick with 3D printed knob and enclosure. 1 $79.95 Link Gamers

    Comments:

Permalink: http://lucidtronix.com/tutorials/27

Tutorials

Display bitmap images and text files and folders with this TFT LCD screen and SD Card navigator....
Our take on the classic bouncing ball, paddle game....
Print bitmaps, cellular automata, drawings and text.....
Handheld gaming machine, based on the Arduino Leonardo, equipped with joystick, SD card and more....
3d view of a face using an SD card, accelerometer and a TFT LCD Screen...
Servo motor, JPEG camera, and Arduino Leonardo combine to make a panoramic camera....
Spherical_camera_main2

Spherical Camera

Arduino-powered spherical camera with 2 Servo Motors and an SD Card....
Use the Joy Gamer to make a dynamic picture frame....