MathJax 3

Thursday, 10 September 2015

Image processing in C: enbossing filter for PPM images

This is the last post of the series about image manipulation for C/C++ beginners. We will see how to create a simple filter to process an image using C. The filter we will implement is named 'embossing filter'. We can find this type of filter image in manipulation programs and image packages like GIMP, Photoshop and ImageMagick.


The embossing filter implementation


 We will write a function that takes a pointer to an image as an argument and returns another pointer to a filtered image. As a start, the function calculates the difference between each pixel channel's value and the value of the channels of its upper left neighbour. Then saves the highest absolute difference, adding to it 128. If there are multiple equal differences with different signs (e.g. -3 and 3), the function will favour the red channel's difference first, then the greens, then the blues. A value above 255 and below 0 will be set to 255 or 0, respectively. 


 The following code is the C implementation of the embossing filter:



Image * emboss_image(Image * img)
{

    //get size of image
    unsigned int w = getImgWidth(img);
    unsigned int h = getImgHeight(img);




    //create a new image to return
    Image * emb_img = new_image(w, h);
    

    Pixel diff;
    Pixel upleft;
    Pixel curr;
    char maxDiff, tmp = 0;
    unsigned char v;

 
    //for each pixel
    int x, y;
    for(y=1; y<h; y++)
    {
        for(x=1; x<w; x++)
        {
                
                 if(maxDiff > tmp)
                      tmp = maxDiff;
                 else
                      maxDiff = tmp;
                
                 //get upper-left pixel value
                 upleft = getPixel(img, x-1,y-1);

                 //get current pixel value
                 curr = getPixel(img, x, y);

                 //find difference between channels
                 diff.r = curr.r - upleft.r;
                 diff.g = curr.g - upleft.g;
                 diff.b = curr.b - upleft.b;


                 //for equal values choose in favor of red
                 if((abs(diff.r)==diff.g && diff.g > diff.b)||(abs(diff.r)==diff.b && diff.b > diff.g))
                      maxDiff = diff.r;


                 //or find the maximum value
                 else
                      maxDiff = max(diff.r,max(diff.g,diff.b));
                 

                 //add 128 to the maximum difference
                 v = 128 + maxDiff;

                 //remove values above 255 or below 0
                 if(v<0)v=0;
                 if(v>255)v=255;


                 //create and set the pixel in emb_img
                 Pixel val2 = {v,v,v};
                 setPixel(x,y,&emb_img,val2);
        }
    }
    return emb_img;
}




Loading a PPM image in memory 


Before we can process an image, we require the capacity to load and retrieve this from our computer's memory. Luckily for us, achieving this with a PPM image, it's simple. Simple as reading a text file. The following C function reads a PPM image's data and stores it inside an Image structure. Then, returns a pointer to this structure:




Image * read_PPM(const char *filename)
{
     char buff[16];
     Image * img;
     int ch;
     int rgb;
   
     //open and read image file
     FILE * fp = fopen(filename, "rb");
     if (!fp) {
          fprintf(stderr, "Unable to open file '%s'\n", filename);
          exit(1);
     }

     //read image format
     if (!fgets(buff, sizeof(buff), fp)) {
          perror(filename);
          fclose(fp);
          exit(1);
     }

    //parse magic number
    if (buff[0] != 'P' || buff[1] != '6') {
         fprintf(stderr, "Invalid image format (must be 'P6')\n");
         fclose(fp);
         exit(1);
    }

    //parse comment
    ch = getc(fp);
    while (ch == '#') {
        while (getc(fp) != '\n') ;
             ch = getc(fp);
    }
    ungetc(ch, fp);
  
    int width, height;
    //parse width and height
    if (fscanf(fp, "%d %d", & width, & height) != 2) {
         fprintf(stderr, "Invalid image size (error loading '%s')\n", filename);
         exit(1);
    }

    //parse max rgb
    if (fscanf(fp, "%d", &rgb) != 1) {
         fprintf(stderr, "Invalid rgb component (error loading '%s')\n", filename);
         fclose(fp);
         exit(1);
    }

    //check max rgb value correctness
    if (rgb !=  RGB) {
         fprintf(stderr, "'%s' is not a 24-bits image.\n", filename);
         fclose(fp);
         exit(1);
    }

   
    //create new image
    img = new_image(width, height);
  
    int bytes;
    //read image data from file and store it inside pixel array
    if ((bytes=fread(img->data, 3 * img->width, img->height, fp)) != img->height) {
         fprintf(stderr, "Error loading image '%s'\n", filename);
         exit(1);
    }
    
    fclose(fp);
    return img;
}


By reading the comment in the code, it should be easy to understand what is happening. We start by opening the PPM image which we want to process. Once we did that, we need to parse the header, which will provide us with all the information's we need to process the image, as the size and the maximum value for the intensity of the colour. Also, we check for comments, as they can be present in a PPM image file, after the magic number. After we finished reading the header, we will create a new image and will store in it the data from the file. At this point, the file can be closed and we can return a pointer to our Image structure. 

If you have read the emboss function, you may have noticed that we call a function called getPixel. With this function we retrieve the RGB value of each pixel stored into the Image structure pointed to by the Image pointer argument :




  
  Pixel  getPixel(Image * i, int x, int y){ 
    return i->data[getImgWidth(i)*y + x]; 
  }


As you can see, the function returns an element of the pixel array of the image passed as an argument. The second and the third parameters represent the coordinates of the pixel in the image plane. If the index used in the array looks weird for you, just remember that if we store the pixels of an image in a 1d array, by using the x y image plane pixel's coordinate, we can access a pixel in the 1d array by indexing this with the integer resulting from the following calculation:

                            
                          pixel position = image_width * y + x   


from this page you can copy or download the complete code for this post. Below you can see the image before and after applying the emboss filter:



                     before                      after

 Conclusions


The image formats implemented in the Netpbm package, and in particular, the PPM format, represent a great and easy media to work with, especially for beginners who wish to start their journey to learning the art of computer graphics, to whom this post series was dedicated.

 

Tuesday, 8 September 2015

Create an image and a pixel structure for PPM images in C

This is the third of a series of posts dedicated to the C/C++ beginners who want to have some fun by working with images, without having to go through the fuss of learning some image manipulation library. It's my opinion that learning to program by getting some kind of visual feedback from it, improves the experience and facilitates the task a lot. Thought, if you are a complete C beginner or if C is the first language you are learning, handling anything outside of the standard library, might confuse and discourage you.

This is where the Netbpm image formats come handy, and this is why I'm using them in this series. They don't require us to install any library, thus we don't need to learn any new API, so we can concentrate on the task of learning C or C++, and on play with image processing or whatever we like to do with an image.

We already saw, in the previous post , how to create a small PPM image, one of the image formats implemented in the Netpbm package. But I bet that, if you are reading this post, you wish to do something more than just creating a randomly coloured image.

A PPM image is an RGB image, so it has three channels for each one of its pixels. We can easily represent this in code by using a structure:

    
typedef struct {
    unsigned char r,g,b;
}Pixel;



an image is made of  pixels (and a lot of them), which form a quadrilateral shape. So, the next natural step in our coding,  it's to write another structure to represent this shape:


 typedef struct {
    unsigned int width, height;
    Pixel * data;   
}Image;




the data field, it's a pointer used to read and to write the values in the array of pixels forming our  quadrilateral shape, or more precisely, our image, the size of which will be width x height. Remember that each pixel has three channels, which we have represented in the Pixel structure with the fields, r, g and b. In a PPM image, each channel contains 1 byte of data(8 bits). This makes the size of the image equals to width x height x 3 bytes large, plus few bytes for the header.

In the previous post we used a nested loop to write the data of each pixel. Now that we have structured our data, we can write a function which will help us to write more efficient code. But first we are going to write two helper functions, so our code will be more readable:


int getImgWidth(Image * i){ return i->width; }
int getImgHeight(Image * i){ return i->height; }



the next function will make it very easy for us to set the value of any of the pixels in the image, without traversing the image with a loop until the desired pixel's position, first:


void setPixel(int x, int y, Image ** data, Pixel value)
{
    Pixel * pixel = (*data)->data;
    pixel[x + y * getImgWidth(*data)].r =
value.r;
    pixel[x + y * getImgWidth(*data)].g =
value.g;
    pixel[x + y * getImgWidth(*data)].b =
value.b;

}



The first two parameter represent the coordinate of the pixel. The third parameter is the image which the pixel belongs to. We need a pointer to pointer because when in C we want to modify the argument of a function, we need to pass it by reference, and that is, we need to pass the address of the "thing" we want to modify, hence a pointer to it. Now, because we already handle our image with a pointer (we will see this later), to modify the image through the function, we need a pointer to a pointer parameter.
The fourth parameter is the new value for the pixel to.

The following is a very stripped version of the main function for the test program: I removed error checking and other stuff, to make more clear to the beginner what is happening in the code:



int main() {

  char filename[80] = "myimage.ppm";
  //create and open image file
  FILE * fp = fopen(filename, "wb");

  //allocate immage memory
  Image * i = malloc(sizeof(Image));

  //initialize image structure
  i->width = 256;
  i->height = 256;

  //allocate pixels memory
  i->data = malloc(sizeof(Pixel) * getImgWidth(i) * getImgHeight(i));

  


  memset(i->data, 255, sizeof(Pixel) * getImgWidth(i) *   getImgHeight(i));

  //initialize a pixel to red
  Pixel red = {255,0,0};
  //and set with it the central pixel of the image
  setPixel(getImgWidth(i)/2, getImgHeight(i)/2, &i, red);

  //write the image header to the file
  fprintf(fp, "P6 %d\n %d\n 255\n", getImgWidth(i), getImgHeight(i));
 
  //write the image data to file
  fwrite(i->data,
sizeof(Pixel), getImgWidth(i) * getImgHeight(i), fp);

  fclose(fp);
  return 0;



If we go through the code, it's quite intuitive and easy to read. We create a new file for the image and open it, then we allocate memory and initialize the variables storing its size (width and height). After we allocate memory for the pixel array. With memset, we fill all the element of the pixel array with the value 255. Then, we initialize a pixel to red which we will use in our setPixel function to paint the image's central pixel. 

Below, the image generated by the code:


One pixel image


In the browser it's hard to see, but if you look well at the image generated by the code on your computer, you will notice that the central pixel is painted in red. From this link you can download the complete source for this post.

Thursday, 3 September 2015

Create colour images using only standard C

In this tutorial we will see how to work with PPM images. For those of you who have missed the previous post, PPM refers to one of the image formats implemented in the Netpbm package.

In the previous post, we saw how to create a very small PGM image using a text file. We could have been doing it with any other programming language, as PGM is in text format. We will use C to create a PPM image, but before we must understand the structure of this format.

In a PGM image, each pixel is represented with one numerical value. With PPM , things change a little. Because PPM is an RGB format, we need three numerical value to represent the three channels of each pixel. If we write the data directly to a file, for three red pixels, after the header, we need to write:

  
255 0 0 255 0 0 255 0 0 ... 

The magic number for a PPM image is P3, if the file is written in ascii mode, and P6 if it's written directly in binary. The following is a program that create a 5 by 3 PPM image containing random colours:

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

int main()
{
  FILE * file = fopen("image.ppm", "wb");
 
  if(!file)
    {
      printf("Error! Cannot open file image.ppm");
      exit(EXIT_FAILURE);
    }

  int width = 5;
  int height = 3;
  int i, j;
  

  //header
  fprintf(file, "P6\n%d %d\n255\n", width, height);
 
  for(i=0; i<height; i++)
    for(j=0; j<width; j++)
      {
        fputc((random()%255), file);//red

        fputc((random()%255), file);//green     
        fputc((random()%255), file);//blue
      }
  fclose(file);
  return 0;
}


The image below is a scaled version of the one generated by the code above on my machine:




In the next post we will do some image processing, using only a PPM image and our best friend language, C. 

Tuesday, 1 September 2015

Working with images in C and C++ for beginners: the Netpbm image format

C and C++ don't have any standard library to work with graphics, GUI or media, diversely from some other programming languages, which support natively such kinds of facilities. Nevertheless, a myriad of open source libraries written in C and C++ does exist, which can be used to manipulate virtually any image or media type and to build GUIs for any kind of device or platform.


 However, using third-party libraries makes writing a program that operates on images, for the C/C++ beginner, a little tricky. This is due, in part, to the complexity of the installation and configuration process of these libraries, in part, to the new API which a user must learn before using them.


 But, if you are a beginner and all you want is to experiment with images in C or C++, regardless of the image's format, there's an easy shortcut to it.




The Netpbm image formats


 In the Netpbm package are implemented various portable image formats, supported by all major platforms. What makes these formats great for beginners, is the fact that they can be used without installing any development library, thus without learning any new API.


 In fact, in the Netbpm image formats, data can be represented in Ascii code, which makes creating an image file as easy as writing to a text file. Data can also be written directly in binary mode, using always the standard C/C++ functions, normally used to write text to a common text stream.


 The main image types among the Netpbm are three: PBM, PGM and PPM.

 The P stands for "portable", while the M stands for "map". The B, the G and P in the middle, stand for "bitmap", "grey" and "pixmap", respectively. Also, there is an alpha and an arbitrary depth format.


 The header section of a Netpbm image is very minimalistic and simple. It is made by a magic number, used as a descriptor for the type of data, and some values, defining the size of the image and the maximum number of colours in it.


 The header attributes are as follow:


image extensionmagic numberimage colours
PBMP1(ascii) P4(binary)2 black/white
PGMP2(ascii) P5(binary)255 Greys
PPMP3(ascii) P6(binary)255 RGB

So, for example, if we wanted to create an image with a width of 7 and with a height of 6 containing only grey pixels, with a value between 0 and 255, we'd write the following header:

P2            
7 6
255


Soon after the header, we can start writing the image's data, inserting each pixel's value in the exact order in which it appears on the screen

0000000
02552552552552550
02551281281282550
02551281281282550
02552552552552550
0000000
Save the above text in a file and name myimage.pgm, and you have a nice greyscale Netpbm image.

 Now, this is a very small image, but it's big enough to understand how "it" works. The one below is a scaled version:





In the next post, we will see how it's simple to create cool images using C or C++.