BugelNiels / improcc

A simple image-processing framework written in C

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool


ImprocC

A simple image-processing framework for C.

Documentation Status

About the project

ImprocC is a simple image-processing framework for C. The framework supports only the netpbm format. This means it supports grayscale .pgm images, binary .pbm and rgb .ppm images. The aim of this framework is to make it easy to save, load, view and manipulate images. The entire framework is located in the files improc.h, improc.c, greyimviewer, and rgbimviewer.c.

Documentation

The documentation of the functions of these functions can be found in improc.h and in the wiki.

Before you start

To familiarize yourself with the framework, take a look at improc.h. This file contains the signatures of all the functions in the framework. You should not need to look at, or modify improc.c, greyimviewer.c and rgbimviewer.c.

Important: When using the framework, try not to access any of the struct values directly. Only use the provided getter/setter functions.

Image Viewer

The framework comes with a built-in image viewer. This image viewer makes use of OpenGL, but it can be disabled at compile time if your machine does not support this. See the Running section. The image viewer has the following functionality:

  • A reset the aspect ratio
  • C contrast stretch*
  • H histogram equalization*
  • I invert image
  • S print statistics
  • T threshold*
  • F random image (false coloring)
  • O show origin in red*
  • G change back to default LUT*
  • R reset viewer
  • Q quit the viewer
  • scroll scale image up/down
  • ctrl+scroll increase/decrease threshold*

* only for the grayscale image viewer.

Pre-requisites

To run, it needs the following:

  • OpenGL
     sudo apt-get install mesa-utils
  • GLUT
     sudo apt-get install freeglut3-dev

Running

To compile and run the program, do the following:

make
./improc <args>

To compile without the provided image viewer, use the following:

make NOVIEW=1

If you want to increase the performance of the framework, you can disable the safety checks on the domains and dynamic range by compiling with:

make RELEASE=1

If you want to remove warnings from the console output, you can do this by compiling with:

make DISABLE_WARNINGS=1

Any of the above flags can be combined.

Overview

Below is a brief overview of all function signatures available in the framework. You can read the full documentation of all the functions here. This image-processing framework is different from most others in the sense that it works with image domains. The indexing of images does not necessarily need to start at 0 and end at size, but you can specify custom ranges. For example, you can specify an image that you can index in the range of [-1, 1] instead of the default [0, size). This is extremely useful for, among other things, convolution kernels.

By default, the getPixel functions will index based on the domain. This means that the index 0 is not guaranteed to be the first pixel. However, sometimes it is useful to be able to have this constraint, regardless of the domain. As such, there are also the getPixelI variants which allow you to index from 0, regardless of the specified domain. More details about this can be found in the documentation.


Image Domains

Every image has a domain. A domain has information about the range of the x values, the range of the y values, and the width and height of the domain. Note that the ranges are inclusive, so if you loop over an image using the ranges (and not the width/height), make sure to use <= in your for loops and not <.

Note that any image is allocated row-wise, so make sure to loop over the images in row-major order for performance reasons.

int getMinX(ImageDomain domain);
int getMaxX(ImageDomain domain);
int getMinY(ImageDomain domain);
int getMaxY(ImageDomain domain);
int getWidth(ImageDomain domain);
int getHeight(ImageDomain domain);
void getWidthHeight(ImageDomain domain, int *width, int *height);
void getImageDomainValues(ImageDomain domain, int *minX, int *maxX, int *minY, int *maxY);
int isInDomain(ImageDomain domain, int x, int y);
int isInDomainI(ImageDomain domain, int x, int y);

Int Images

IntImages are images that consist of only integer values. The integer values that an int image can have depends on its dynamic range.

Allocation

IntImage allocateIntImageGrid(int minX, int maxX, int minY, int maxY, int minValue, int maxValue);
IntImage allocateIntImageGridDomain(ImageDomain domain, int minValue, int maxValue);
IntImage allocateIntImage(int width, int height, int minValue, int maxValue);
IntImage allocateDefaultIntImage(int width, int height);
IntImage allocateFromIntImage(IntImage image);
IntImage copyIntImage(IntImage image);
void freeIntImage(IntImage image);

Getters + Setters

ImageDomain getIntImageDomain(IntImage image);
void getMinMax(IntImage image, int *minimalValue, int *maximalValue);
void getDynamicRange(IntImage image, int *minRange, int *maxRange);
int getIntPixel(IntImage image, int x, int y);
int getIntPixelI(IntImage image, int x, int y);

void setIntPixel(IntImage *image, int x, int y, int greyValue);
void setIntPixelI(IntImage *image, int x, int y, int greyValue);
void setAllIntPixels(IntImage *image, int greyValue);
void setDynamicRange(IntImage *image, int newMinRange, int newMaxRange);

Printing + Viewing

void printIntBuffer(IntImage image);
void printIntImageLatexTable(IntImage image);
void printIntLatexTableToFile(FILE *out, IntImage image);
void displayIntImage(IntImage image, const char *windowTitle);

Saving + Loading

IntImage loadIntImage(const char *path);
void saveIntImage(IntImage image, const char *path);
void saveIntImagePGMRaw(IntImage image, const char *path);
void saveIntImagePGMAscii(IntImage image, const char *path);
void saveIntImagePBMRaw(IntImage image, const char *path);
void saveIntImagePBMAscii(IntImage image, const char *path);

General Operations

IntImage distanceTransform(IntImage image, int metric, int foreground);

IntImage maxIntImage(IntImage imageA, IntImage imageB);
IntImage minIntImage(IntImage imageA, IntImage imageB);
IntImage addIntImage(IntImage imageA, IntImage imageB);
IntImage subtractIntImage(IntImage imageA, IntImage imageB);
IntImage multiplyIntImage(IntImage imageA, IntImage imageB);
IntImage applyLutIntImage(IntImage image, int *LUT, int LUTSize);
IntImage dilateIntImageRect(IntImage image, int kw, int kh);
IntImage erodeIntImageRect(IntImage image, int kw, int kh);

Transformations

IntImage padIntImage(IntImage image, int top, int right, int bottom, int left, int padValue);
void translateIntImage(IntImage *image, int x, int y);
void flipIntImageHorizontal(IntImage *image);
void flipIntImageVertical(IntImage *image);

RGB Images

RgbImages are images that consist of 3 channels: red, green and blue. Each of the channels are represented by integer values. The integer values that a channel can have depends on its dynamic range.

Allocation

RgbImage allocateRgbImageGrid(int minX, int maxX, int minY, int maxY, int minValue, int maxValue);
RgbImage allocateRgbImageGridDomain(ImageDomain domain, int minValue, int maxValue);
RgbImage allocateRgbImage(int width, int height, int minValue, int maxValue);
RgbImage allocateDefaultRgbImage(int width, int height);
RgbImage allocateFromRgbImage(RgbImage image);
RgbImage copyRgbImage(RgbImage image);
void freeRgbImage(RgbImage image);

Getters + Setters

ImageDomain getRgbImageDomain(RgbImage image);
void getRgbDynamicRange(RgbImage image, int *minRange, int *maxRange);
void getRgbPixel(RgbImage image, int x, int y, int *r, int *g, int *b);
void getRgbPixelI(RgbImage image, int x, int y, int *r, int *g, int *b);

void setRgbPixel(RgbImage *image, int x, int y, int r, int g, int b);
void setRgbPixelI(RgbImage *image, int x, int y, int r, int g, int b);
void setAllRgbPixels(RgbImage *image, int r, int g, int b);

Printing + Viewing

void printRgbBuffer(RgbImage image);
void printRgbImageLatexTable(RgbImage image);
void printRgbLatexTableToFile(FILE *out, RgbImage image);
void displayRgbImage(RgbImage image, const char *windowTitle);

Saving + Loading

RgbImage loadRgbImage(const char *path);
void saveRgbImage(RgbImage image, const char *path);
void saveRgbImagePPMRaw(RgbImage image, const char *path);
void saveRgbImagePPMAscii(RgbImage image, const char *path);

General Operations

RgbImage maxRgbImage(RgbImage imageA, RgbImage imageB);
RgbImage minRgbImage(RgbImage imageA, RgbImage imageB);
RgbImage addRgbImage(RgbImage imageA, RgbImage imageB);
RgbImage subtractRgbImage(RgbImage imageA, RgbImage imageB);
RgbImage multiplyRgbImage(RgbImage imageA, RgbImage imageB);
RgbImage applyLutRgbImage(RgbImage image, int **LUT, int LUTsize);

Transformations

RgbImage padRgbImage(RgbImage image, int top, int right, int bottom, int left, int r, int g, int b);
void translateRgbImage(RgbImage *image, int x, int y);
void flipRgbImageHorizontal(RgbImage *image);
void flipRgbImageVertical(RgbImage *image);

Complex Images

ComplexImages are images that consist complex values. These images do not have a dynamic range. When these complex images are viewed, only the real values are dispalyed. Note that these will also be clamped between 0 and 255, so make sure to do any rescaling beforehand.

Allocation

ComplexImage allocateComplexImageGrid(int minX, int maxX, int minY, int maxY);
ComplexImage allocateComplexImageGridDomain(ImageDomain domain);
ComplexImage allocateComplexImage(int width, int height);
ComplexImage allocateFromComplexImage(ComplexImage image);
ComplexImage copyComplexImage(ComplexImage image);
void freeComplexImage(ComplexImage image);

Getters + Setters

ImageDomain getComplexImageDomain(ComplexImage image);
void getComplexMinMax(ComplexImage image, double *minimalValue, double *maximalValue);
double complex getComplexPixel(ComplexImage image, int x, int y);
double complex getComplexPixelI(ComplexImage image, int x, int y);

void setComplexPixel(ComplexImage *image, int x, int y, double complex complexValue);
void setComplexPixelI(ComplexImage *image, int x, int y, double complex complexValue);
void setAllComplexPixels(ComplexImage *image, double complex complexValue);

Printing + Viewing

void printComplexBuffer(ComplexImage image);
void printComplexImageLatexTable(ComplexImage image);
void printComplexLatexTableToFile(FILE *out, ComplexImage image);
void displayComplexImage(ComplexImage image, const char *windowTitle);

Saving + Loading

void saveComplexImage(ComplexImage image, const char *path);
void saveComplexImagePGMRaw(ComplexImage image, const char *path);
void saveComplexImagePGMAscii(ComplexImage image, const char *path);

General Operations

ComplexImage fft2D(IntImage image);
IntImage ifft2D(ComplexImage image);
ComplexImage fft2DDouble(DoubleImage image);
DoubleImage ifft2DDouble(ComplexImage image);
void fft2Dshift(ComplexImage *image);
void ifft2Dshift(ComplexImage *image);
ComplexImage multiplyComplexImage(ComplexImage imageA, ComplexImage imageB);

Double Images

DoubleImages are images that consist of double values. These images cannot be displayed directly, so they will need to be explicitly converted to IntImages first.

Allocation

DoubleImage allocateDoubleImageGrid(int minX, int maxX, int minY, int maxY, double minValue, double maxValue);
DoubleImage allocateDoubleImageGridDomain(ImageDomain domain, double minValue, double maxValue);
DoubleImage allocateDoubleImage(int width, int height, double minValue, double maxValue);
DoubleImage allocateFromDoubleImage(DoubleImage image);
DoubleImage allocateDefaultDoubleImage(int width, int height);
DoubleImage copyDoubleImage(DoubleImage image);
void freeDoubleImage(DoubleImage image);

Getters + Setters

ImageDomain getDoubleImageDomain(DoubleImage image);
void getDoubleDynamicRange(DoubleImage image, double *minRange, double *maxRange);
void getDoubleMinMax(DoubleImage image, double *minimalValue, double *maximalValue);
double getDoublePixel(DoubleImage image, int x, int y);
double getDoublePixelI(DoubleImage image, int x, int y);

void setDoublePixel(DoubleImage *image, int x, int y, double val);
void setDoublePixelI(DoubleImage *image, int x, int y, double val);
void setAllDoublePixels(DoubleImage *image, double val);

Printing + Viewing

void printDoubleBuffer(DoubleImage image);
void printDoubleImageLatexTable(DoubleImage image);
void printDoubleLatexTableToFile(FILE *out, DoubleImage image);
// for viewing, you can convert it to an intImage first

General Operations

DoubleImage int2DoubleImg(IntImage image);
IntImage double2IntImg(DoubleImage image);

Histograms

There is also very simple support for histograms. These histograms can only be constructed for IntImages and RgbImages.

Creation

Histogram createEmptyHistogram(int minRange, int maxRange);
Histogram createHistogram(IntImage image);
void createRgbHistograms(RgbImage image, Histogram *redHist, Histogram *greenHist, Histogram *blueHist);
void freeHistogram(Histogram histogram);

Operations

void getHistogramRange(Histogram histogram, int *minRange, int *maxRange);
int getHistogramFrequency(Histogram histogram, int pixelVal);

void setHistogramFrequency(Histogram *histogram, int pixelVal, int freq);
void incrementHistogramFrequency(Histogram *histogram, int pixelVal);

void printHistogram(Histogram histogram);

Example Code Snippets

Below you can find a code snippet containing some example code. This snippet will load an image from the provided path and threshold it at different thresholds. Every stage is displayed and saved.

void thresholdDemoDomain(const char *path) {
  // Set up
  IntImage image = loadIntImage(path);
  ImageDomain domain = getIntImageDomain(image);
  displayIntImage(image, "Source Image");
  int minX, maxX, minY, maxY;
  getImageDomainValues(domain, &minX, &maxX, &minY, &maxY);
  IntImage thresholdedImage = allocateIntImage(getWidth(domain), getHeight(domain), 0, 255);

  // Threshold image with different thresholds
  for (int threshold = 64; threshold < 256; threshold += 64) {
    for (int y = minY; y <= maxY; y++) {
      for (int x = minX; x <= maxX; x++) {
        if (getIntPixel(image, x, y) < threshold) {
          setIntPixel(&thresholdedImage, x, y, 0);
        } else {
          setIntPixel(&thresholdedImage, x, y, 255);
        }
      }
    }
    char filename[20];
    sprintf(filename, "threshold%d.pbm", threshold);
    displayIntImage(thresholdedImage, filename);  // filename is used as window title
    saveIntImage(thresholdedImage, filename);
  }

  // Clean up
  freeIntImage(thresholdedImage);
  freeIntImage(image);
}

It is also possible to do the indexing by starting at 0. That would look as follows:

void thresholdDemoIndexed(const char *path) {
  IntImage image = loadIntImage(path);
  ImageDomain domain = getIntImageDomain(image);
  displayIntImage(image, "Source Image");
  int width, height;
  getWidthHeight(domain, &width, &height);
  IntImage thresholdedImage = allocateIntImage(width, height, 0, 255);
  for (int threshold = 64; threshold < 256; threshold += 64) {
    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        if (getIntPixelI(image, x, y) < threshold) {
          setIntPixelI(&thresholdedImage, x, y, 0);
        } else {
          setIntPixelI(&thresholdedImage, x, y, 255);
        }
      }
    }
    char filename[20];
    sprintf(filename, "threshold%d.pbm", threshold);
    displayIntImage(thresholdedImage, filename);  // filename is used as window title
    saveIntImage(thresholdedImage, filename);
  }
  // Clean up
  freeIntImage(thresholdedImage);
  freeIntImage(image);
}

Note the usage of the I variants of getPixel and setPixel. However, in this case, this is equivalent to the regular versions, since the domain starts at 0 by default.

About

A simple image-processing framework written in C


Languages

Language:C 99.3%Language:Makefile 0.7%