Posts Tagged ‘Dr. Dobbs’

Image Morph Windows App

November 30, 2006

I resurrected source code from a Windows App that I wrote back in 1993.

The app uses a source image, an 8X8 source mesh, a destination image and an 8X8 destination mesh to generate a series of morphing images. I will try to post a revised version of the program soon.

It is written mostly in C. The image morph algorithm, called "meshwarp", was taken from a 1993 Dr. Dobbs Journal article. The meshwarp code was written by George Wolberg and you can read more about it here.

The attached image shows the mesh editor functionality that I wrote before MFC was the defacto standard.

Here’s a snippet of George Wolberg’s meshwarp code.

/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* meshWarp:
*
* Warp I1 with correspondence points given in meshes M1 and M2.
* Result goes in I2.
*
* See "Digital Image Warping" by George Wolberg (IEEE Computer Society
* Press, 1990) for details.
* Based on Douglas Smythe’s algorithm (in "A Two-Pass Mesh Warping Algorithm
* for Object Transformation and Image Interpolation", ILM Technical Memo
* #1030, 1990).
*/
void meshWarp( imageP I1, imageP M1, imageP M2, imageP I2 )
{
  int I_w, I_h, M_w, M_h;
  int x, y, u, v, n;
  float *x1, *y1, *x2, *y2;
  float *xrow, *yrow, *xcol, *ycol, *coll, *indx, *map;
  uchar *src, *dst;
  imageP Mx, My, I3;

  I_w = I1->width;
  I_h = I1->height;

  M_w = M1->width;
  M_h = M1->height;

  /* allocate enough memory for a scanline along the longest dimension */
  n = MAX(I_w, I_h);
  indx = (float *) WINmalloc(n * sizeof(float));
  xrow = (float *) WINmalloc(n * sizeof(float));
  yrow = (float *) WINmalloc(n * sizeof(float));
  map = (float *) WINmalloc((n*2) * sizeof(float));

  /* create table of x-intercepts for source mesh’s vertical splines */
  Mx = allocImage(M_w, I_h, MESH);
  for(y=0; y < I_h; y++) indx[y] = y;
  for(u=0; u < M_w; u++) { /* visit each vertical spline */
    /* store column as row for spline fct */
    xcol = (float *) M1->ch[0] + u;
    ycol = (float *) M1->ch[1] + u;
    coll = (float *) Mx->ch[0] + u;

    /* scan convert vertical splines */
    for(v=0; v < M_h; v++, xcol+=M_w) xrow[v] = *xcol;
    for(v=0; v < M_h; v++, ycol+=M_w) yrow[v] = *ycol;
    catmullRom(yrow, xrow, M_h, indx, map, I_h);

    /* store resampled row back into column */
    for(y=0; y < I_h; y++, coll+=M_w) *coll = map[y];
  }

  /* create table of x-intercepts for dst mesh’s vertical splines */
  for(u=0; u < M_w; u++) { /* visit each vertical spline */
    /* store column as row for spline fct */
    xcol = (float *) M2->ch[0] + u;
    ycol = (float *) M2->ch[1] + u;
    coll = (float *) Mx->ch[1] + u;

    /* scan convert vertical splines */
    for(v=0; v < M_h; v++, xcol+=M_w) xrow[v] = *xcol;
    for(v=0; v < M_h; v++, ycol+=M_w) yrow[v] = *ycol;
    catmullRom(yrow, xrow, M_h, indx, map, I_h);

    /* store resampled row back into column */
    for(y=0; y < I_h; y++, coll+=M_w) *coll = map[y];
  }

  /* first pass: warp x using tables in Mx */
  SendGenStatus( "1st Pass: Interpolating and Resampling…" );

  I3 = allocImage(I_w+1, I_h+1, BW); // +1 for testing purposes
  x1 = (float *) Mx->ch[0];
  x2 = (float *) Mx->ch[1];
  src = (uchar *) I1->ch[0];
  dst = (uchar *) I3->ch[0];
  for(x=0; x < I_w; x++) indx[x] = x;
  for(y=0; y < I_h; y++) {
    /* fit spline to x-intercepts; resample over all cols */

  catmullRom(x1, x2, M_w, indx, map, I_w);

    /* resample source row based on map */
    resample(src, I_w, 1, map, dst);

    /* advance pointers to next row */
    src += I_w;
    dst += I_w;
    x1 += M_w;
    x2 += M_w;
  }
  freeImage(Mx);

  /* create table of y-intercepts for intermediate mesh’s hor splines */
  SendGenStatus("Interpolating horizontal splines of intermediate mesh…");
  My = allocImage(I_w, M_h, MESH);
  x1 = (float *) M2->ch[0];
  y1 = (float *) M1->ch[1];
  y2 = (float *) My->ch[0];
  for(x=0; x < I_w; x++) indx[x] = x;
  for(v=0; v < M_h; v++) { /* visit each horizontal spline */
    /* scan convert horizontal splines */
    catmullRom(x1, y1, M_w, indx, y2, I_w);

    /* advance pointers to next row */
    x1 += M_w;
    y1 += M_w;
    y2 += I_w;
  }

  /* create table of y-intercepts for dst mesh’s horizontal splines */
  SendGenStatus("Interpolating horizontal splines of destination mesh…");
  x1 = (float *) M2->ch[0];
  y1 = (float *) M2->ch[1];
  y2 = (float *) My->ch[1];
  for(v=0; v < M_h; v++) { /* visit each horizontal spline */
    /* scan convert horizontal splines */
    catmullRom(x1, y1, M_w, indx, y2, I_w);

    /* advance pointers to next row */
    x1 += M_w;
    y1 += M_w;
    y2 += I_w;
  }

  /* second pass: warp y */
  SendGenStatus( "2nd Pass: Interpolating and Resampling…" );

  src = (uchar *) I3->ch[0];
  dst = (uchar *) I2->ch[0];
  for(y=0; y < I_h; y++) indx[y] = y;
  for(x=0; x < I_w; x++) {
    /* store column as row for spline fct */
    xcol = (float *) My->ch[0] + x;
    ycol = (float *) My->ch[1] + x;
    for(v=0; v < M_h; v++, xcol+=I_w) xrow[v] = *xcol;
    for(v=0; v < M_h; v++, ycol+=I_w) yrow[v] = *ycol;

    /* fit spline to y-intercepts; resample over all rows */
    catmullRom(xrow, yrow, M_h, indx, map, I_h);

    /* resample source column based on map */
    resample(src, I_h, I_w, map, dst);

    /* advance pointers to next column */
    src++;
    dst++;
  }

  freeImage(My);
  freeImage(I3);

  WINfree((char *) indx);
  WINfree((char *) xrow);
  WINfree((char *) yrow);
  WINfree((char *) map);
}

Morph1