R2Image *
CreateGaussianImage(double sigma) 
{
  // Get useful variables
  if (sigma <= 0) return NULL;
  const double sqrt_of_two_pi = 2.506628274631;
  double a = 1.0 / (sigma * sqrt_of_two_pi);
  double b = -1.0 / (2.0 * sigma * sigma);
  int radius = (int) (3.0*sigma + 0.5);
  int width = 2 * radius + 1;

  // Create image
  R2Image *image = new R2Image(width, width, 1);
  if (!image) return NULL;

  // Fill image
  for (int i = -radius; i <= radius; i++) {
    double g1 = a*exp(b*i*i);
    for (int j = -radius; j <= radius; j++) {
      double g2 = a*exp(b*j*j);
      double value = g1 * g2;
      image->SetValue(i + radius, j + radius, 0, value);
    }
  }

  // Return image
  return image;
}
R2Image *RenderImage(R3Scene *scene, int width, int height, int max_depth,
  int num_primary_rays_per_pixel, int num_distributed_rays_per_intersection)
{
  // Allocate  image
  R2Image *image = new R2Image(width, height);
  if (!image) {
    fprintf(stderr, "Unable to allocate image\n");
    return NULL;
  }

  // Calculate each pixel value
  for (int i = 0; i < width; i++)
  {
    cout << i << endl;
    for (int j = 0; j < height; j++)
    {
      // Cast primary ray through the pixel
      R3Ray ray = RayThoughPixel(scene->Camera(), i, j, width, height);
      // Compute the radiance traveling into the camera along that ray
      R3Rgb rgb = ComputeRadiance(scene, ray, 0, max_depth, num_distributed_rays_per_intersection);
      // set image pixel value
      image->SetPixel(i, j, rgb);
    }
  }

  // Return image
  return image;
}
void R2Image::
Multiply(const R2Image& image) 
{
  // Multiply by image pixel-by-pixel
  assert(image.Width() == Width());
  assert(image.Height() == Height());
  assert(image.NChannels() == NChannels());
  int nvalues = NValues();
  for (int i = 0; i < nvalues; i++) {
    pixels[i] *= image.pixels[i];
  }
}
void R2Image::
Subtract(const R2Image& image) 
{
  // Subtract image pixel-by-pixel
  assert(image.Width() == Width());
  assert(image.Height() == Height());
  assert(image.NChannels() == NChannels());
  int nvalues = NValues();
  for (int i = 0; i < nvalues; i++) {
    pixels[i] -= image.pixels[i];
  }
}
void R2Image::
Divide(const R2Image& image) 
{
  // Add image pixel-by-pixel
  assert(image.Width() == Width());
  assert(image.Height() == Height());
  assert(image.NChannels() == NChannels());
  int nvalues = NValues();
  for (int i = 0; i < nvalues; i++) {
    if (image.pixels[i] == 0) {
      if (pixels[i] == 0.0) continue;
      else pixels[i] = FLT_MAX;
    }
    else {
      pixels[i] /= image.pixels[i];
    }
  }
}
static R2Image *
ReadImage(const char *filename)
{
    // Allocate a image
    R2Image *image = new R2Image();
    if (!image) {
        fprintf(stderr, "Unable to allocate image");
        return NULL;
    }
    
    // Read image
    if (!image->Read(filename)) {
        fprintf(stderr, "Unable to read image file %s", filename);
        return NULL;
    }
    
    // Print message
    if (print_verbose) {
        printf("Read image from %s\n", filename);
        printf("  Resolution = %d %d\n", image->Width(), image->Height());
        printf("  L1Norm = %g\n", image->L1Norm());
        printf("  L2Norm = %g\n", image->L2Norm());
        fflush(stdout);
    }
    
    // Return image
    return image;
}
void R2Image::
Filter(const R2Image& filter) 
{
  // Get useful variables
  int xr = filter.Width()/2;
  int yr = filter.Height()/2;
  R2Image copy(*this);

  // This is the straight-forward implementation (slow for large filters)
  for (int j = 0; j < Height(); j++) {
    for (int i = 0; i < Width(); i++) {
      for (int c = 0; c < NChannels(); c++) {
        int fc = (NChannels() == filter.NChannels()) ? c : 0;

        // Compute new value 
        double sum = 0;
        for (int s = 0; s < filter.Width(); s++) {
          int x = i - xr + s;
          if (x < 0) x = 0;
          else if (x >= Width()) x = Width()-1;
          for (int t = 0; t < filter.Height(); t++) {
            int y = j - yr + t;
            if (y < 0) y = 0;
            else if (y >= Height()) y = Height()-1;
            double filter_value = filter.Value(s, t, fc);
            double image_value = copy.Value(x, y, c);
            sum += filter_value * image_value;
          }
        }

        // Assign new value for channel
        SetValue(i, j, c, sum);
      }
    }
  }
}
int 
main(int argc, char **argv)
{
  // Look for help
  for (int i = 0; i < argc; i++) {
    if (!strcmp(argv[i], "-help")) {
      ShowUsage();
    }
  }

  // Read input and output filenames
  if (argc < 3)  ShowUsage();
  argv++, argc--; // First argument is program name
  char *input_scene_name = *argv; argv++, argc--; 
  char *output_image_name = *argv; argv++, argc--; 

  // Initialize arguments to default values
  int width = 256;
  int height = 256;
  int max_depth = 0;
  int num_distributed_rays_per_intersection = 0;
  int num_primary_rays_per_pixel = 1;
	bool hard_shadow = false;

  // Parse arguments 
  while (argc > 0) {
    if (!strcmp(*argv, "-width")) {
      CheckOption(*argv, argc, 2);
      width = atoi(argv[1]);
      argv += 2, argc -= 2;
    }
    else if (!strcmp(*argv, "-height")) {
      CheckOption(*argv, argc, 2);
      height = atoi(argv[1]);
      argv += 2, argc -= 2;
    }
    else if (!strcmp(*argv, "-max_depth")) {
      CheckOption(*argv, argc, 2);
      max_depth = atoi(argv[1]);
      argv += 2, argc -= 2;
    }
    else if (!strcmp(*argv, "-antialias")) {
      CheckOption(*argv, argc, 2);
      num_primary_rays_per_pixel = atoi(argv[1]);
      argv += 2, argc -= 2;
    }
    else if (!strcmp(*argv, "-distribute")) {
      CheckOption(*argv, argc, 2);
      num_distributed_rays_per_intersection = atoi(argv[1]);
      argv += 2, argc -= 2;
    }
		else if (!strcmp(*argv, "-hard_shadow")) {
			hard_shadow = true;
			argv += 1, argc -= 1;
		}
    else {
      // Unrecognized program argument
      fprintf(stderr,  "meshpro: invalid option: %s\n", *argv);
      ShowUsage();
    }
  }

  // Read scene
  R3Scene *scene = ReadScene(input_scene_name, width, height);
  if (!scene) {
    fprintf(stderr, "Unable to read scene from %s\n", input_scene_name);
    exit(-1);
  }

  // Render image
  R2Image *image = RenderImage(scene, width, height, max_depth, 
    num_primary_rays_per_pixel, num_distributed_rays_per_intersection, hard_shadow);
  if (!image) {
    fprintf(stderr, "Did not render image from scene\n");
    exit(-1);
  }

  // Transfer the image to sRGB color space: for Windows + Linux and Mac OS X
  // 10.6+ (for earlier MAC OS X it will look slightly too bright, but not as
  // much as it would be too dark otherwise. This function also clamps the 
  // image values; however, it does not scale the brightness and also does not
  // perform any more complicated tone mapping
  image->TosRGB();

  // Write output image
  if (!image->Write(output_image_name)) {
    fprintf(stderr, "Did not write image to %s\n", output_image_name);
    exit(-1);
  }

  // Delete everything
  delete scene;
  delete image;

  // Return success
  return EXIT_SUCCESS;
}
Exemple #9
0
int R3Mesh::
ReadImage(const char *filename)
{
  // Create a mesh by reading an image file, 
  // constructing vertices at (x,y,luminance), 
  // and connecting adjacent pixels into faces. 
  // That is, the image is interpretted as a height field, 
  // where the luminance of each pixel provides its z-coordinate.

  // Read image
  R2Image *image = new R2Image();
  if (!image->Read(filename)) return 0;

  // Create vertices and store in arrays
  R3MeshVertex ***vertices = new R3MeshVertex **[image->Width() ];
  for (int i = 0; i < image->Width(); i++) {
    vertices[i] = new R3MeshVertex *[image->Height() ];
    for (int j = 0; j < image->Height(); j++) {
      double luminance = image->Pixel(i, j).Luminance();
      double z = luminance * image->Width();
      R3Point position((double) i, (double) j, z);
      R2Point texcoords((double) i, (double) j);
      vertices[i][j] = CreateVertex(position, R3zero_vector, texcoords);
    }
  }

  // Create faces
  vector<R3MeshVertex *> face_vertices;
  for (int i = 1; i < image->Width(); i++) {
    for (int j = 1; j < image->Height(); j++) {
      face_vertices.clear();
      face_vertices.push_back(vertices[i-1][j-1]);
      face_vertices.push_back(vertices[i][j-1]);
      face_vertices.push_back(vertices[i][j]);
      face_vertices.push_back(vertices[i-1][j]);
      CreateFace(face_vertices);
    }
  }

  // Delete vertex arrays
  for (int i = 0; i < image->Width(); i++) delete [] vertices[i];
  delete [] vertices;

  // Delete image
  delete image;

  // Return success
  return 1;
}
void LoadMaterial(R3Material *material) 
{
  GLfloat c[4];

  // Check if same as current
  static R3Material *current_material = NULL;
  if (material == current_material) return;
  current_material = material;

  // Compute "opacity"
  double opacity = 1 - material->kt.Luminance();

  // Load ambient
  c[0] = material->ka[0];
  c[1] = material->ka[1];
  c[2] = material->ka[2];
  c[3] = opacity;
  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, c);

  // Load diffuse
  c[0] = material->kd[0];
  c[1] = material->kd[1];
  c[2] = material->kd[2];
  c[3] = opacity;
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, c);

  // Load specular
  c[0] = material->ks[0];
  c[1] = material->ks[1];
  c[2] = material->ks[2];
  c[3] = opacity;
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, c);

  // Load emission
  c[0] = material->emission.Red();
  c[1] = material->emission.Green();
  c[2] = material->emission.Blue();
  c[3] = opacity;
  glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, c);

  // Load shininess
  c[0] = material->shininess;
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, c[0]);

  // Load texture
  if (material->texture) {
    if (material->texture_index <= 0) {
      // Create texture in OpenGL
      GLuint texture_index;
      glGenTextures(1, &texture_index);
      material->texture_index = (int) texture_index;
      glBindTexture(GL_TEXTURE_2D, material->texture_index); 
      R2Image *image = material->texture;
      int npixels = image->NPixels();
      R2Pixel *pixels = image->Pixels();
      GLfloat *buffer = new GLfloat [ 4 * npixels ];
      R2Pixel *pixelsp = pixels;
      GLfloat *bufferp = buffer;
      for (int j = 0; j < npixels; j++) { 
        *(bufferp++) = pixelsp->Red();
        *(bufferp++) = pixelsp->Green();
        *(bufferp++) = pixelsp->Blue();
        *(bufferp++) = pixelsp->Alpha();
        pixelsp++;
      }
      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
      glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
      glTexImage2D(GL_TEXTURE_2D, 0, 4, image->Width(), image->Height(), 0, GL_RGBA, GL_FLOAT, buffer);
      delete [] buffer;
    }

    // Select texture
    glBindTexture(GL_TEXTURE_2D, material->texture_index); 
    glEnable(GL_TEXTURE_2D);
  }
  else {
    glDisable(GL_TEXTURE_2D);
  }

  // Enable blending for transparent surfaces
  if (opacity < 1) {
    glDepthMask(false);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
  }
  else {
    glDisable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ZERO);
    glDepthMask(true);
  }
}
Exemple #11
0
int R3Model::
ReadObjMtlFile(const char *dirname, const char *mtlname)
{
  // Open file
  char filename[1024];
  sprintf(filename, "%s/%s", dirname, mtlname);
  FILE *fp = fopen(filename, "r");
  if (!fp) {
    RNFail("Unable to open file %s", filename);
    return 0;
  }

  // Parse file
  char buffer[1024];
  int line_count = 0;
  R3Brdf *brdf = NULL;
  R2Texture *texture = NULL;
  R3Material *material = NULL;
  while (fgets(buffer, 1023, fp)) {
    // Increment line counter
    line_count++;

    // Skip white space
    char *bufferp = buffer;
    while (isspace(*bufferp)) bufferp++;

    // Skip blank lines and comments
    if (*bufferp == '#') continue;
    if (*bufferp == '\0') continue;

    // Get keyword
    char keyword[80];
    if (sscanf(bufferp, "%s", keyword) != 1) {
      RNFail("Syntax error on line %d in file %s", line_count, filename);
      return 0;
    }

    // Check keyword
    if (!strcmp(keyword, "newmtl")) {
      // Parse line
      char name[1024];
      if (sscanf(bufferp, "%s%s", keyword, name) != (unsigned int) 2) {
        RNFail("Syntax error on line %d in file %s", line_count, filename);
        return 0;
      }

      // Create new material
      texture = NULL;
      brdf = new R3Brdf();
      material = new R3Material(brdf, texture, name);
      materials.Insert(material);
      RNArray<R3Triangle *> *mat_tris = new RNArray<R3Triangle *>();
      material_triangles.Insert(mat_tris);
    }
    else if (!strcmp(keyword, "Ka")) {
      // Parse line
      double r, g, b;
      if (sscanf(bufferp, "%s%lf%lf%lf", keyword, &r, &g, &b) != (unsigned int) 4) {
        RNFail("Syntax error on line %d in file %s", line_count, filename);
        return 0;
      }

      // Set ambient reflectance 
      if (material && brdf) {
        brdf->SetAmbient(RNRgb(r, g, b));
        material->Update();
      }
    }
    else if (!strcmp(keyword, "Kd")) {
      // Parse line
      double r, g, b;
      if (sscanf(bufferp, "%s%lf%lf%lf", keyword, &r, &g, &b) != (unsigned int) 4) {
        RNFail("Syntax error on line %d in file %s", line_count, filename);
        return 0;
      }

      // Set diffuse reflectance 
      if (material && brdf) {
        brdf->SetDiffuse(RNRgb(r, g, b));
        material->Update();
      }
    }
    else if (!strcmp(keyword, "Ks")) {
      // Parse line
      double r, g, b;
      if (sscanf(bufferp, "%s%lf%lf%lf", keyword, &r, &g, &b) != (unsigned int) 4) {
        RNFail("Syntax error on line %d in file %s", line_count, filename);
        return 0;
      }

      // Set specular reflectance 
      if (material && brdf) {
        brdf->SetSpecular(RNRgb(r, g, b));
        material->Update();
      }
    }
    else if (!strcmp(keyword, "Ns")) {
      // Parse line
      double ns;
      if (sscanf(bufferp, "%s%lf", keyword, &ns) != (unsigned int) 2) {
        RNFail("Syntax error on line %d in file %s", line_count, filename);
        return 0;
      }

      // Set shininess
      if (material && brdf) {
        brdf->SetShininess(ns);
        material->Update();
      }
    }
    else if (!strcmp(keyword, "Ni")) {
      // Parse line
      double index_of_refraction;
      if (sscanf(bufferp, "%s%lf", keyword, &index_of_refraction) != (unsigned int) 2) {
        RNFail("Syntax error on line %d in file %s", line_count, filename);
        return 0;
      }

      // Set index of refraction
      if (material && brdf) {
        brdf->SetIndexOfRefraction(index_of_refraction);
        material->Update();
      }
    }
    else if (!strcmp(keyword, "d")) {
      // Parse line
      double transparency;
      if (sscanf(bufferp, "%s%lf", keyword, &transparency) != (unsigned int) 2) {
        RNFail("Syntax error on line %d in file %s", line_count, filename);
        return 0;
      }

      // Set opacity
      if (material && brdf) {
        brdf->SetOpacity(1 - transparency);
        material->Update();
      }
    }
    else if (!strcmp(keyword, "map_Kd")) {
      // Parse line
      char texture_name[1024];
      if (sscanf(bufferp, "%s%s", keyword, texture_name) != (unsigned int) 2) {
        RNFail("Syntax error on line %d in file %s", line_count, filename);
        return 0;
      }

      // Set texture
      if (material) {
        char texture_filename[1024];
        sprintf(texture_filename, "%s/%s", dirname, texture_name);
        R2Image *image = new R2Image();
        if (!image->Read(texture_filename)) return 0;
        R2Texture *texture = new R2Texture(image);
        material->SetTexture(texture);
        material->Update();
      }
    }
  }

  // Close file
  fclose(fp);

  // Return success
  return 1;
}
Exemple #12
0
int 
main(int argc, char **argv)
{
  // Look for help
  for (int i = 0; i < argc; i++) {
    if (!strcmp(argv[i], "-help")) {
      ShowUsage();
    }
	if (!strcmp(argv[i], "-svdTest")) {
      R2Image *image = new R2Image();
	  image->svdTest();
	  return 0;
    }
  }

  // Read input and output image filenames
  if (argc < 3)  ShowUsage();
  argv++, argc--; // First argument is program name
  char *input_image_name = *argv; argv++, argc--; 
  char *output_image_name = *argv; argv++, argc--; 

  // Allocate image
  R2Image *image = new R2Image();
  if (!image) {
    fprintf(stderr, "Unable to allocate image\n");
    exit(-1);
  }

  // Read input image
  if (!image->Read(input_image_name)) {
    fprintf(stderr, "Unable to read image from %s\n", input_image_name);
    exit(-1);
  }

  // Initialize sampling method
  int sampling_method = R2_IMAGE_POINT_SAMPLING;

  // Parse arguments and perform operations 
  while (argc > 0) {
    if (!strcmp(*argv, "-brightness")) {
      CheckOption(*argv, argc, 2);
      double factor = atof(argv[1]);
      argv += 2, argc -=2;
      image->Brighten(factor);
    }
	else if (!strcmp(*argv, "-sobelX")) {
      argv++, argc--;
      image->SobelX();
    }
	else if (!strcmp(*argv, "-sobelY")) {
      argv++, argc--;
      image->SobelY();
    }
	else if (!strcmp(*argv, "-log")) {
      argv++, argc--;
      image->LoG();
    }
    else if (!strcmp(*argv, "-saturation")) {
      CheckOption(*argv, argc, 2);
      double factor = atof(argv[1]);
      argv += 2, argc -= 2;
      image->ChangeSaturation(factor);
    }
	else if (!strcmp(*argv, "-harris")) {
      CheckOption(*argv, argc, 2);
      double sigma = atof(argv[1]);
      argv += 2, argc -= 2;
      image->Harris(sigma);
    }
    else if (!strcmp(*argv, "-blur")) {
      CheckOption(*argv, argc, 2);
      double sigma = atof(argv[1]);
      argv += 2, argc -= 2;
      image->Blur(sigma);
    }
    else if (!strcmp(*argv, "-sharpen")) {
      argv++, argc--;
      image->Sharpen();
    }
    else if (!strcmp(*argv, "-matchTranslation")) {
      CheckOption(*argv, argc, 2);
      R2Image *other_image = new R2Image(argv[1]);
      argv += 2, argc -= 2;
      image->blendOtherImageTranslated(other_image);
      delete other_image;
    }
    else if (!strcmp(*argv, "-matchHomography")) {
      CheckOption(*argv, argc, 2);
      R2Image *other_image = new R2Image(argv[1]);
      argv += 2, argc -= 2;
      image->blendOtherImageHomography(other_image);
      delete other_image;
    }
    else {
      // Unrecognized program argument
      fprintf(stderr, "image: invalid option: %s\n", *argv);
      ShowUsage();
    }
  }

  // Write output image
  if (!image->Write(output_image_name)) {
    fprintf(stderr, "Unable to read image from %s\n", output_image_name);
    exit(-1);
  }

  // Delete image
  delete image;

  // Return success
  return EXIT_SUCCESS;
}
Exemple #13
0
int 
main(int argc, char **argv)
{
  // Look for help
  for (int i = 0; i < argc; i++) {
    if (!strcmp(argv[i], "-help")) {
      ShowUsage();
    }
  }

  // Read input and output image filenames
  if (argc < 3)  ShowUsage();
  argv++, argc--; // First argument is program name
  char *input_image_name = *argv; argv++, argc--; 
  char *output_image_name = *argv; argv++, argc--; 

  // Allocate image
  R2Image *image = new R2Image();
  if (!image) {
    fprintf(stderr, "Unable to allocate image\n");
    exit(-1);
  }

  // Read input image
  if (!image->Read(input_image_name)) {
    fprintf(stderr, "Unable to read image from %s\n", input_image_name);
    exit(-1);
  }

  // Initialize sampling method
  int sampling_method = R2_IMAGE_POINT_SAMPLING;

  // Parse arguments and perform operations 
  while (argc > 0) {
    if (!strcmp(*argv, "-bilateral")) {
      CheckOption(*argv, argc, 3);
      double sx = atof(argv[1]);
      double sy = atof(argv[2]);
      argv += 3, argc -= 3;
      image->BilateralFilter(sy, sx);
    } 
    else if (!strcmp(*argv, "-blackandwhite")) {
      argv++, argc--;
      image->BlackAndWhite();
    }
    else if (!strcmp(*argv, "-blur")) {
      CheckOption(*argv, argc, 2);
      double sigma = atof(argv[1]);
      argv += 2, argc -= 2;
      image->Blur(sigma);
    }
    else if (!strcmp(*argv, "-brightness")) {
      CheckOption(*argv, argc, 2);
      double factor = atof(argv[1]);
      argv += 2, argc -=2;
      image->Brighten(factor);
    }
    else if (!strcmp(*argv, "-composite")) {
      CheckOption(*argv, argc, 5);
      R2Image *top_image = new R2Image(argv[2]);
      R2Image *bottom_mask = new R2Image(argv[1]);
      R2Image *top_mask = new R2Image(argv[3]);
      int operation = atoi(argv[4]);
      argv += 5, argc -= 5;
      image->CopyChannel(*bottom_mask, R2_IMAGE_BLUE_CHANNEL, R2_IMAGE_ALPHA_CHANNEL);
      top_image->CopyChannel(*top_mask, R2_IMAGE_BLUE_CHANNEL, R2_IMAGE_ALPHA_CHANNEL);
      image->Composite(*top_image, operation);
      delete top_image;
      delete bottom_mask;
      delete top_mask;
    }
    else if (!strcmp(*argv, "-contrast")) {
      CheckOption(*argv, argc, 2);
      double factor = atof(argv[1]);
      argv += 2, argc -= 2;
      image->ChangeContrast(factor);
    }
    else if (!strcmp(*argv, "-crop")) {
      CheckOption(*argv, argc, 5);
      int x = atoi(argv[1]);
      int y = atoi(argv[2]);
      int w = atoi(argv[3]);
      int h = atoi(argv[4]);
      argv += 5, argc -= 5;
      image->Crop(x, y, w, h);
    }
    else if (!strcmp(*argv, "-dither")) {
      CheckOption(*argv, argc, 3);
      int dither_method = atoi(argv[1]);
      int nbits = atoi(argv[2]);
      argv += 3, argc -= 3;
      if (dither_method == 0) image->RandomDither(nbits);
      else if (dither_method == 1) image->OrderedDither(nbits);
      else if (dither_method == 2) image->FloydSteinbergDither(nbits);
      else { fprintf(stderr, "Invalid dither method: %d\n", dither_method); exit(-1); }
    }
    else if (!strcmp(*argv, "-edge")) {
      argv++, argc--;
      image->EdgeDetect();
    } 
    else if (!strcmp(*argv, "-extract")) {
      CheckOption(*argv, argc, 2);
      int channel = atoi(argv[1]);
      argv += 2, argc -= 2;
      image->ExtractChannel(channel);
    }
    else if (!strcmp(*argv, "-fun")) {
      image->Fun(sampling_method);
      argv++, argc--;
    }
    else if (!strcmp(*argv, "-gamma")) {
      CheckOption(*argv, argc, 2);
      double factor = atof(argv[1]);
      argv += 2, argc -= 2;
      image->ApplyGamma(factor);
    }
    else if (!strcmp(*argv, "-median")) {
      CheckOption(*argv, argc, 2);
      double sigma = atof(argv[1]);
      argv += 2, argc -= 2;
      image->MedianFilter(sigma);
    }
    else if (!strcmp(*argv, "-motionblur")) {
      CheckOption(*argv, argc, 1);
      int amount = atoi(argv[1]);
      argv += 2, argc -= 2;
      image->MotionBlur(amount);
    }
    else if (!strcmp(*argv, "-morph")) {
      int nsegments = 0;
      R2Segment *source_segments = NULL;
      R2Segment *target_segments = NULL;
      CheckOption(*argv, argc, 4);
      R2Image *target_image = new R2Image(argv[1]);
      ReadCorrespondences(argv[2], source_segments, target_segments, nsegments);
      double t = atof(argv[3]);
      argv += 4, argc -= 4;
      image->Morph(*target_image, source_segments, target_segments, nsegments, t, sampling_method);
      delete target_image;
    }
    else if (!strcmp(*argv, "-noise")) {
      CheckOption(*argv, argc, 2);
      double factor = atof(argv[1]);
      argv += 2, argc -= 2;
      image->AddNoise(factor);
    }
    else if (!strcmp(*argv, "-quantize")) {
      CheckOption(*argv, argc, 2);
      int nbits = atoi(argv[1]);
      argv += 2, argc -= 2;
      image->Quantize(nbits);
    }
    else if (!strcmp(*argv, "-rotate")) {
      CheckOption(*argv, argc, 2);
      double angle = atof(argv[1]);
      argv += 2, argc -= 2;
      image->Rotate(angle, sampling_method);
    }
    else if (!strcmp(*argv, "-sampling")) {
      CheckOption(*argv, argc, 2);
      sampling_method = atoi(argv[1]);
      argv += 2, argc -= 2;
    }
    else if (!strcmp(*argv, "-saturation")) {
      CheckOption(*argv, argc, 2);
      double factor = atof(argv[1]);
      argv += 2, argc -= 2;
      image->ChangeSaturation(factor);
    }
    else if (!strcmp(*argv, "-scale")) {
      CheckOption(*argv, argc, 3);
      double sx = atof(argv[1]);
      double sy = atof(argv[2]);
      argv += 3, argc -= 3;
      image->Scale(sx, sy, sampling_method);
    }
    else if (!strcmp(*argv, "-sharpen")) {
      argv++, argc--;
      image->Sharpen();
    }
    else {
      // Unrecognized program argument
      fprintf(stderr, "image: invalid option: %s\n", *argv);
      ShowUsage();
    }
  }

  // Write output image
  if (!image->Write(output_image_name)) {
    fprintf(stderr, "Unable to write image to %s\n", output_image_name);
    exit(-1);
  }

  // Delete image
  delete image;

  // Return success
  return EXIT_SUCCESS;
}