Code

/*
Anisotropic Shader
Uses the sobel filter to extract edges, and then gaussian blurs within edge borders.
Creates a kinda watercolored or cell-shaded effect.
Could be used as a first step in image segmentation.
--Max
*/

float t[] = {0.17,0.08,0.13};

void setup()
{
  size(600,600,P2D);
  colorMode(RGB,1.0);
  PImage I;
  float[][][] data;
  float[][] edges;

  for (int i=0; i<3; i++)
  {
    I = loadImage("row"+i+".jpg");
    data = new float[3][I.width][I.height];
    for (int x=0; x<I.width; x++)
    for (int y=0; y<I.height; y++)
    {
      data[RED][x][y] = red(I.get(x,y));
      data[GRN][x][y] = green(I.get(x,y));
      data[BLU][x][y] = blue(I.get(x,y));
    }
    edges = canny_edge_detection(data);
    for (int x=0; x<data[0].length; x++)
    for (int y=0; y<data[0][0].length; y++)
    {
      set(x,y+200*i,color(data[RED][x][y],data[GRN][x][y],data[BLU][x][y]));
      if (edges[x][y] > t[i])
        set(x+data[0].length,y+200*i,color(1.0));
      else
        set(x+data[0].length,y+200*i,color(0.0));
    }
    for (int j=0; j<50; j++)
      data = anisotropic_filter(data,edges,t[i]);
    for (int x=0; x<data[0].length; x++)
    for (int y=0; y<data[0][0].length; y++)
      set(x+2*data[0].length,y+200*i,color(data[RED][x][y],data[GRN][x][y],data[BLU][x][y]));
  }
}

final int X_RESP = 0;
final int Y_RESP = 1;
final int RED = 0;
final int GRN = 1;
final int BLU = 2;


float[][][] anisotropic_filter(float[][][] I, float[][] E, float threshold)
{
  float sr,sg,sb,count;
  float[][][] R = new float[3][I[0].length][I[0][0].length];
  for (int x=0; x<I[0].length; x++)
  for (int y=0; y<I[0][0].length; y++)
  {
    sr = I[RED][x][y];
    sg = I[GRN][x][y];
    sb = I[BLU][x][y];
    count = 1.0;
    if (y-1>=0 && E[x][y-1] < threshold)
    {
      sr += I[RED][x][y-1];
      sg += I[GRN][x][y-1];
      sb += I[BLU][x][y-1];
      count += 1.0;
    }
    if (y+1<I[0][0].length && E[x][y+1] < threshold)
    {
      sr += I[RED][x][y+1];
      sg += I[GRN][x][y+1];
      sb += I[BLU][x][y+1];
      count += 1.0;
    }
    if (x-1>=0 && E[x-1][y] < threshold)
    {
      sr += I[RED][x-1][y];
      sg += I[GRN][x-1][y];
      sb += I[BLU][x-1][y];
      count += 1.0;
    }
    if (x+1<I[0].length && E[x+1][y] < threshold)
    {
      sr += I[RED][x+1][y];
      sg += I[GRN][x+1][y];
      sb += I[BLU][x+1][y];
      count += 1.0;
    }
    R[RED][x][y] = sr/count;
    R[GRN][x][y] = sg/count;
    R[BLU][x][y] = sb/count;
  }
  return R;
}

void normalize_kernel(float[][] kernel)
{
  float fac = float(kernel.length*kernel[0].length);
  float sum = 0.0;
  for (int x=0; x<kernel.length; x++)
  for (int y=0; y<kernel[0].length; y++)
    sum += kernel[x][y];
  for (int x=0; x<kernel.length; x++)
  for (int y=0; y<kernel[0].length; y++)
    kernel[x][y]*=fac/sum;
}

float[][] convolve(float[][] I, float[][]kernel)
{
  float[][] out = new float[I.length][I[0].length];
  int dx = -(kernel.length-1)>>1;
  int dy = -(kernel[0].length-1)>>1;
  int ix,iy,kx,ky;
  float c;
  for (ix=0; ix<I.length; ix++)
  for (iy=0; iy<I[0].length; iy++)
  {
    c = 0.0;
    for (kx=0; kx<kernel.length; kx++)
    for (ky=0; ky<kernel[0].length; ky++)
    if ((ix+kx+dx>=0)&&(iy+ky+dy>=0) &&(ix+kx+dx<I.length) &&(iy+ky+dy<I[0].length))
    {
        out[ix][iy] += kernel[kx][ky]*I[ix+kx+dx][iy+ky+dy];
        c += 1.0;
    }
    out[ix][iy]/=c;
  }
  return out;
}

float[][] canny_edge_detection(float[][][] I)
{
  float[][] gaussian_kernel = {{0,1,0},
                               {1,2,1},
                               {0,1,0}};
  float[][] sobel_x_kernel = {{-1,0,1},
                              {-2,0,2},
                              {-1,0,1}};
  float[][] sobel_y_kernel = {{1,2,1},
                              {0,0,0},
                              {-1,-2,-1}};
  
  float[][][][] edgeResponse = new float[2][3][][];

  normalize_kernel(gaussian_kernel);
  edgeResponse[X_RESP][RED] = convolve(I[RED],gaussian_kernel);
  edgeResponse[Y_RESP][RED] = convolve(I[RED],gaussian_kernel);
  edgeResponse[X_RESP][GRN] = convolve(I[GRN],gaussian_kernel);
  edgeResponse[Y_RESP][GRN] = convolve(I[GRN],gaussian_kernel);
  edgeResponse[X_RESP][BLU] = convolve(I[BLU],gaussian_kernel);
  edgeResponse[Y_RESP][BLU] = convolve(I[BLU],gaussian_kernel);
  edgeResponse[X_RESP][RED] = convolve(I[RED],sobel_x_kernel);
  edgeResponse[Y_RESP][RED] = convolve(I[RED],sobel_y_kernel);
  edgeResponse[X_RESP][GRN] = convolve(I[GRN],sobel_x_kernel);
  edgeResponse[Y_RESP][GRN] = convolve(I[GRN],sobel_y_kernel);
  edgeResponse[X_RESP][BLU] = convolve(I[BLU],sobel_x_kernel);
  edgeResponse[Y_RESP][BLU] = convolve(I[BLU],sobel_y_kernel);
  
  float[][][] power = new float[3][I[0].length][I[0][0].length];
  for (int x=0; x<I[0].length; x++)
  for (int y=0; y<I[0][0].length; y++)
  for (int c=0; c<3; c++)
  {
    power[c][x][y] = pow( sq(edgeResponse[X_RESP][c][x][y]) +
                          sq(edgeResponse[Y_RESP][c][x][y]), 0.4 );
  }

  float[][] rval = new float[I[0].length][I[0][0].length];
  float mn = 0.0, mx = 0.0;
  
  for (int x=0; x<I[0].length; x++)
  for (int y=0; y<I[0][0].length; y++)
  {
    rval[x][y] = sqrt( sq(power[RED][x][y]) + sq(power[GRN][x][y]) +
                       sq(power[BLU][x][y]) );
    if (rval[x][y] > mx)
      mx = rval[x][y];
    if (rval[x][y] < mn)
      mn = rval[x][y];
  }
  for (int x=0; x<I[0].length; x++)
  for (int y=0; y<I[0][0].length; y++)
  {
    rval[x][y] -= mn;
    rval[x][y] /= (mx-mn);
  }  
      
  return rval;  
}

042 -- Pixels II: Content-Based Image Filtering: Due 10/20

Statement:Find a photograph whose dimensions are roughly 400x400. Create a program which applies a custom filter to this image. Your program could be time-based, or static.

Read up about image filtering at the HIPR. Your filter could be a simple linear convolution, such as a blur, gradient, edge-detection, etc., or it could be a filter with non-linear effects on pixel neighborhoods, such as a median-filter, erosion/dilation, or something of your own design. Indeed, it is not strictly necessary for your filter to use pixel-rewriting: your filter could instead be accomplished with a process in which the original photo becomes warped, or over-written by synthetic graphics.

Ok, now the twist: develop your program so that it applies this filter "selectively" or "intelligently", depending on some kind of image content that you are able to detect algorithmically.

For some hypothetical explanatory illustrations of what I mean: You could write a program to turn all blueberries into redberries, without otherwise changing their surrounding muffin. Or you could write a program to detect the ridgepole (top edge) of a house's roof, and automatically place bird images there. Or you could write a program that used color-criteria to find people's faces; and you could then selectively blur those face-pixels in order to hide people's identities. Or you could somehow detect people's eyes, and then use this knowledge to warp the size of their eyes like a cartoon character. These are just examples.
To put this another way, try to think of easily-detectable classes of pixel-based phenomena; then try to think of image-based transformations that would be conceptually interesting to apply to those phenomena, exclusively.
An Anisotropic Shader

hide statement