-6

I need a help about histogram equalization in RGB image files for my academic coursework.

I checked my previous code samples about histogram equalization and I did not find any clue about this issue. I've never practiced a histogram equalization example which is RGB image.

The image is PPM file. So, we need to convert the file from RGB to YCbCr and from RGB to HSI.

Then, we need to do the histogram equalization while the image is in the YCbCr and HSI format.

After that, we'll need to convert the PPM file to RGB format again. That's it.

*void write_image function is writing the data to the pnr.ppm*

*void get_image_data function is getting the image that is mandrill1.ppm*

We need to only specify the code:

#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
#include<ctype.h>
#include<string.h>
#include <fcntl.h>
#include <malloc.h>
#include <math.h>
#define PI 3.1415926535897932384626433832795
struct ppm_header
{
   char pgmtype1;
   char pgmtype2;
   int pwidth;
   int pheight;
   int pmax;
};
struct ppm_file
{
   struct ppm_header *pheader;
   unsigned char *rdata,*gdata,*bdata;
};

void get_image_data(char *filename,struct ppm_file *image);
void write_image(char *filename,struct ppm_file *image);

main()
{
  struct ppm_file resim;
  get_image_data("mandrill1.ppm",&resim);


  printf("pgmtype...=%c%c\n",resim.pheader->pgmtype1,resim.pheader->pgmtype2);
  printf("width...=%d\n",resim.pheader->pwidth);
  printf("height...=%d\n",resim.pheader->pheight);
  printf("max gray level...=%d\n",resim.pheader->pmax);

  write_image("pnr.ppm",&resim);
  return 0;
}

void write_image(char *filename,struct ppm_file *image) 
{
    FILE *fp;
    int i,max=0;
    fp=fopen(filename,"wb");
    fputc(image->pheader->pgmtype1,fp);
    fputc(image->pheader->pgmtype2,fp);
    fputc('\n',fp);
    fprintf(fp,"%d %d\n",image->pheader->pwidth,image->pheader->pheight);
    fprintf(fp,"%d\n",255/*max*/);
    for(i=0;i<image->pheader->pwidth*image->pheader->pheight;i++)
    {
        fwrite(&image->rdata[i],1,1,fp);
        fwrite(&image->gdata[i],1,1,fp);
        fwrite(&image->bdata[i],1,1,fp);
    }
    fclose(fp);
}
void get_image_data(char *filename, struct ppm_file *image )
{
    FILE* fp;
    int i=0;
    char temp[256];
    image->pheader=(struct ppm_header *)malloc(sizeof(struct ppm_header));
    fp = fopen(filename, "rb" );
    if (fp==NULL)
    {
        printf("Dosya acilamadi: %s.\n\n", filename);
        exit(1);
    }
    printf ("Okunan PPM dosyasi : %s...\n", filename);
    fscanf (fp, "%s", temp);
    if (strcmp(temp, "P6") == 0)
    {
        image->pheader->pgmtype1=temp[0];
        image->pheader->pgmtype2=temp[1];
        fscanf (fp, "%s", temp);
        if (temp[0]=='#')
        {

            while(fgetc(fp)!='\n');
            fscanf (fp, "%d %d\n",&image->pheader->pwidth,&image->pheader->pheight);
            fscanf (fp, "%d\n", &image->pheader->pmax);

        }
        else
        {
            sscanf (temp, "%d", &image->pheader->pwidth);
            fscanf (fp, "%d", &image->pheader->pheight);
            fscanf (fp, "%d", &image->pheader->pmax);
        }
        image->rdata=(unsigned char *)malloc(image->pheader->pheight*image->pheader->pwidth*sizeof(unsigned char));
        image->gdata=(unsigned char *)malloc(image->pheader->pheight*image->pheader->pwidth*sizeof(unsigned char));
        image->bdata=(unsigned char *)malloc(image->pheader->pheight*image->pheader->pwidth*sizeof(unsigned char));
        if (image->rdata==NULL) printf("bellek problemi...\n");
        for(i=0;i<image->pheader->pwidth*image->pheader->pheight;i++)
        {
            fread(&image->rdata[i],1,1,fp);
            fread(&image->gdata[i],1,1,fp);
            fread(&image->bdata[i],1,1,fp);
        }
    }
    else
    {
        printf ("\nHata Resim dosyasi PGM P6 formatinda degil");
        exit(1);
    }
    fclose(fp);
}
halfer
  • 19,824
  • 17
  • 99
  • 186
  • I'm afraid that your question is begging to get downvoted and closed. Please remove the grovelling and narrow down your problem area to something manageable. – ThingyWotsit Apr 27 '17 at 20:35
  • Even the PPM reader function (`get_image_data()`) is incorrect. Are you sure you *deserve* to pass the course, without fulfilling the requirements of the course? I am not. – Nominal Animal Apr 27 '17 at 20:40
  • Also - indentation/formatting is bad. Indent, and add a blank line bwtween functions. – ThingyWotsit Apr 27 '17 at 20:46
  • We are neither a code-writing service nor a homework assistance service, and we are absolutely uninterested in helping you obtain a grade you have not earned. Sorry. We will answer *specific* questions about on-topic subject matter, but you don't seem to have any clear question at all. – John Bollinger Apr 27 '17 at 20:57
  • 1
    a) if we help you, we don't help each other because you do nothing for us. b) if this affects your final exam I really wonder how you made it that far in the first place, given the incredible poor quality and style of your post. c) you did not even ask something or formulate a problem you have. you just said what your task is and posted some code. what do you expect us to do with it? you should rather earn things in life. not beg for them... – Piglet Apr 27 '17 at 21:51
  • 1
    would you guys just sit together and solve that problem as a team rather than spamming stack overflow with your homework? what is wrong with todays students? http://stackoverflow.com/questions/43667345/converting-a-ppm-from-rgb-to-hsl-in-c http://stackoverflow.com/questions/43660624/histogram-equalization-for-rgb-image – Piglet Apr 27 '17 at 21:58
  • @JohnBollinger hello. I wanna say that I have a problem with the question. I do not have a knowledge about the subject. So, I need you guys. Please, I need to do and learn this subject. Our teacher did not show the method of the converting from RGB to YCbCr and from RGB to HSI. If you have any knowledge about this, this won't take much longer for you. – raskolnikov Apr 28 '17 at 12:54
  • @WeatherVane there was none a tag option which is named "RGBtoHSI" or "RGBtoYCbCr" or "HistogramComparison" – raskolnikov Apr 28 '17 at 12:55
  • @ThingyWotsit What do you suggest to do this with your style? Because I really need to achieve this problem – raskolnikov Apr 28 '17 at 13:07
  • @NominalAnimal Can you help me about the problem? If you think to do that, I wanna work with people just like you and the others who knows C expertly – raskolnikov Apr 28 '17 at 13:08
  • So it took you **sixteen hours** to respond to comments on your desperately important assignment? – Weather Vane Apr 28 '17 at 16:18
  • 2
    @raskolnikov: I am not sure it is possible to help you. You see, you are here to find an easy solution, one which would let you pass with minimal effort on your part. From my position, that would be the worst possible outcome. In a little while, you would be one more person claiming to be a programmer in a field that is, in my opinion, already inundated with illogical, irrational, lazy un-creative non-thinkers; people worse than cancer for our cultures. If you were to fail here, you might actually have to reconsider your strategy in life! That might be the best outcome here. – Nominal Animal Apr 28 '17 at 16:25
  • @WeatherVane while you were writing your comments, I was trying to solve my problem and reach to people who want to help me unlike you. I'm still desperate thanks to you. – raskolnikov Apr 28 '17 at 17:45
  • @raskolnikov I have sadly misjudged you. I was sure you had dumped your question here and gone off to do something else, returning later to see if anyone has done the work for you. Obviously, I was badly mistaken. But my advice would be to stop going through life expecting others to solve your problems, and then try to blame them. – Weather Vane Apr 28 '17 at 17:51
  • @WeatherVane Sorry to annoy you, man. Because the other comment made me angry. I'm sorry to blame you for your thoughts. Also, I never give up. I always tries to find the right solution for my problem. Right now, I've limited time and the problem is up to me. However, solving the problem is gonna help me about the image-processing, the histogram equalizitaion and of course the lesson. I do not just wanna pass the lesson. I also wanna learn the subject. So, if you wanna help me, I can make a good offer for you. Because I'm in so desperate. I hope you'll help me. Take care :) – raskolnikov Apr 28 '17 at 18:22

1 Answers1

3

Let's look at the problems at the algorithmic level.

  1. Your get_image_data() does not handle the PPM format (Netpbm P6 format) correctly. Just like the other binary Netpbm formats -- PBM, PGM, PPM, PNM --, the P6 format can have comments before the maximum component value (which is followed by exactly one newline, \0, followed by the binary data).

    (Although the Wikipedia Netpbm format article says that a comment is possible even after the maximum component value, that makes the binary formats ambiquous, as a parser cannot tell whether a # (binary \x23) is part of the image data or the start of a comment. So, a lot of utilities do not allow a comment after the last header value at all, to keep the formats unambiguous.)

    To parse the binary Netpbm formats correctly in C, you need to read the two first characters of the file or stream first, to detect the format. The rest of the header values are all nonnegative integers, and can be scanned using a single function that also skips comment lines. If we use the C I/O facilities, then we can write that function easily using the one-character pushback facility; in pseudocode,

    Function pnm_value(stream):
        Read one character from stream into c
        Loop:
            If c == EOF:
                Premature end of input; fail.
            If c == '#':
                Loop:
                    Read one character from stream into c
                    If c is not EOF or '\n', break loop
                End loop
                Continue at the start of the outer loop
            If c is a '\t', '\n', '\v', '\f', '\r', or ' ':
                Read one character from stream into c
                Continue at the start of the outer loop
            Otherwise break loop
        End loop
    
        If c is not a digit:
            Invalid input; fail
    
        Value = 0
        While c is a digit:
            OldValue = Value
            Value = 10*value + (value of digit c)
            If (Value / 10 != OldValue):
                Value is too large; fail
            Read one character from stream into c
        End While
    
        If c is not EOF:
            Push (unget) c back to stream
    
        Return Value
    End function
    

    After you have read the header fields using the above function, for binary formats you should read one more character from the stream or file, and it must be a newline \n for the format to be valid (and unambiguous).

    Binary data can be read in C using getc(stream); there is no need to use fread(). This is faster, because getc() is often a macro (that may evaluate its argument, stream, more than once; it does not harm anything in this particular case).

    For the P6 format, if the maxval field in the header (the third value, after width and height in pixels) is at most 255, there are width×heightx3 chars of data; red component first, then the green, and finally blue.

    If the maxval field is 256 to 65535, there are width×height×6 chars of data in the P6 format. In each set of six characters, the first two are red, the next two green, and the last two blue components; with the most significant byte first.
     

  2. For High Dynamic Range images, including exploration of different color spaces, I recommend using a data structure with 64 bits per pixel, 20 bits per component. For example,

    typedef struct {
        size_t    width;
        size_t    height;
        size_t    stride;   /* Usually == width */
        uint64_t *pixel;    /* i = y*stride + x */
        void     *data;     /* Origin of allocated pixel data */
    } image;
    

    A separate stride lets you allocate the pixel map with extra pixels, in case you wish to e.g. apply a filter kernel to the data; then you do not need to handle border pixels in any special way, just initialize them to appropriate colors (duplicating the image edge pixels, typically).

    When reading PNM files into the above data structure, instead of saving whatever value you read from the file, you compute

    component = (1048575 * file_component) / maxvalue;
    

    for each color component read from the file. This ensures that you always have component values between 0 and 1048575 for each component, regardless of the precision of the component saved in the file.

    In practice, to read a pixel from a P6/PPM file into a 64-bit, 20 bits per component pixel value, you could use e.g.

    uint64_t  pixel;
    uint64_t  red, green, blue;
    
    if (maxval > 255) {
        red = (getc(stream) & 255) << 8;
        red += getc(stream) & 255;
        green = (getc(stream) & 255) << 8;
        green += getc(stream) & 255;
        blue = (getc(stream) & 255) << 8;
        blue += getc(stream) & 255;
    } else {
        red = getc(stream) & 255;
        green = getc(stream) & 255;
        blue = getc(stream) & 255;
    }
    
    pixel = ((uint64_t)((1048575 * red) / maxval) << 40)
          | ((uint64_t)((1048575 * green) / maxval) << 20)
          |  (uint64_t)((1048575 * blue) / maxval);
    

    In your particular case, this is not really important, and indeed you could just read the entire data (3*width*height chars if maxval<=255, 6*width*height chars if maxval>=256) as is, without conversion.
     

  3. There is no need to convert the image data to another color model explicitly: you can compute the histograms while you read the file, and adjust the colors when writing the output file.

    Histogram equalization is an operation where each color component for each pixel is scaled separately, using a simple function that makes the histograms as flat as possible. You can find more practical examples and explanations (like this PDF) with your favourite search engine.

    When you read the red, green, and blue components for a pixel, and scale them to the 0..1048575 range (inclusive), you can calculate the Y/Cb/Cr and H/S/I using the formulae shown on their respective Wikipedia articles, for example. You can do the calculations using integers or floats, but remember that you need to decide the size of your histograms (and therefore eventually convert each component to integer). To avoid quantization error in the color conversions, you should use more bits per component in these "temporary" colorspaces -- say, 24 bits sounds good.

    Whichever colorspace you do use for histogram equalization, you most likely end up converting the histogram into a component mapping; that is, rather than element c[i] describing the number of pixels having this color component of value i, you transform it so that c[i] yields the equalized color component value for original color component value i.
     

  4. When you have the three color component mappings, you can save the output file.

    For each pixel, you convert the red, green, and blue components to the colorspace you use for the histogram equalization. You map each of the components separately. Then, you convert the color components back to the RGB model, and finally save the pixel red, green, and blue components.

    If the original file used a maxval of 255 or less, save the file using a maxval of 255 (and one char per color component). If the original file used a larger maxval, use a maxval of 65535 (and two chars per color component; most significant byte first). Or, better yet, let the user specify the resulting maxval at run time.
     

  5. If the input is from a file, you don't even need to remember the pixel data for the image, as you can simply read it twice.

    Note, however, that most utilities that process Netpbm files are written to allow easy piping. Indeed, that is the most common type of use that I show my fellow users that need e.g. manipulate specific colors or gray levels in an image. Because of this, it is typically recommended to keep the pixel data in memory, and write all errors and information to standard error only.
     

I would estimate that counting in SLOC, your program will mostly consist of the code needed to parse command-line arguments, read the input file, and write the output file. The colorspace conversions are not difficult nor long, and the histogram stuff is near trivial. (After all, you are just counting how many times a specific color component appears in the image.)

Even so, it is most important that you write your program one step at a time. For one, it limits the region of code you need to inspect when a bug occurs.

Rather than working on a single program, I like to use temporary test programs (some might call these unit tests) to implement each part separately, before combining them into the program proper. In your case, I would definitely write the read-PPM-P6-image and write-PPM-P6-image functions first, and test them, for example by rotating the image 180 degrees (so upper left corner will become the lower right corner), or something similar. When you get it working, and you can open your generated PPM/P6 images in Gimp, Netpbm tools, eog, or whatever applications and utilities you might use, only then progress to the rest of the problem.

Also, make your code easy to read. That means consistent indentation. And lots of comments: NOT describing what the code does, but describing what problem the code tries to solve; what task it tries to accomplish.

As it stands, the code shown in your post is a mismash of stuff. You do not even have a clear question in your 'question'! If you progress step by step, implementing and testing each part separately, and don't let your code become an ugly mess, you'll never end up in that situation. Instead, you can ask intelligent questions like how to best merge your disparate parts, if you get lost. (Often that involves rewriting a part using a different view, different "paradigm", but that is a good thing, because then you learn why different views and different tools are useful in different situations, and how to determine the situation.)

Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
  • thanks for your interest. I've misjudged you. Sorry for that. Still, I need you for this problem. My offer for Piglet is still valid for you,too. My offer is money. I'm considering to pay you because of the limited time that I've, if you're gonna solve my problem. That might not be the truthful idea for you. Because your ideas about programming is the right one. I support you. However, I need to achieve the problem even so. So, what do you think about that? Will you help me? I'll wait your answer. By the way, I've limited time for the problem. – raskolnikov Apr 29 '17 at 12:11
  • @raskolnikov dude this guy is giving you free advice so you can actually think about your problem and work on yourself. don't insult him by offering him money so he will push your creepy lazy ass through some exams. – Piglet Apr 29 '17 at 13:19
  • @raskolnikov: Piglet is exactly right. I'd rather eat carnivore poo -- which is way worse than any grazer poo -- than help someone fake their way through education. The ability, and willingess, to *learn* is one of the very few redeeming features our species have. Anyone who thinks they are "above" learning the hard way, deserve nothing but pain, suffering, and disappointment. *"Siberia teaches."* – Nominal Animal Apr 29 '17 at 14:40
  • @NominalAnimal guys. Let's continue to solve the problem step by step. 1-) I need to convert the file from RGB to YCbCr and from RGB to HSI. 2-) I need to do the histogram equalization. 3-) I need to convert the file from HSI and YCbCr to RGB. //// I know how to equalize the Grayscale image. I know second step. I do not know the first step. That's why, you think that I'm lazy. You're right. I could not explain my problem throughly. Sorry for that. What can we do for the first step? Because I did not understand the NominalAnimal's way. Sorry to insult you, guys. – raskolnikov Apr 29 '17 at 16:39
  • @Piglet The above comment interests you,too. – raskolnikov Apr 29 '17 at 16:40
  • @raskolnikov there are formulas for conversion from rbg to hsi/ycbcr and backward. all you have to do is enter hsi rgb into google and you get the solution. you'll even get plenty of sample code. or read through the implementations of open source image processing libraries. Imagine how people studied when there was no internet with unlimited access to infinite amounts of knowledge and examples... they actually had to read books over books and do it the hard way. all YOU have to do is google and copy... – Piglet Apr 29 '17 at 16:49
  • @Piglet /*struct RGB { unsigned char R; unsigned char G; unsigned char B; };*/ struct YCbCr { float Y; float Cb; float Cr; }; struct YCbCr RGBToYCbCr(struct ppm_file rgb) { float fr = (float)rgb.rdata / 255; float fg = (float)rgb.gdata / 255; float fb = (float)rgb.bdata / 255; struct YCbCr ycbcr; ycbcr.Y = (float)(0.2989 * fr + 0.5866 * fg + 0.1145 * fb); ycbcr.Cb = (float)(-0.1687 * fr - 0.3313 * fg + 0.5000 * fb); ycbcr.Cr = (float)(0.5000 * fr - 0.4184 * fg - 0.0816 * fb); return ycbcr; } – raskolnikov Apr 30 '17 at 11:11
  • @Piglet I wanted to show you some code samples that I struggled to implement to achieve my problem. I wrote these codes last night. I do not know how to show you these codes. I tried something – raskolnikov Apr 30 '17 at 11:13
  • @raskolnikov just delete your last post and try again. this time ask an actual question that can be answered. don't beg and don't offer money. read [ask] befor you do so – Piglet Apr 30 '17 at 11:34
  • @NominalAnimal offers great advice on tackling a complex problem like this. BREAK THE BIG PROBLEM INTO SMALL ONES. Create a function that converts PPM to RGB, then a function that converts RGB to PPM. Test them. Easy. Then repeat for the other conversions you need. Then implement the histogram equalization per the PDF you were provided. Then call them in the proper order... – Justin J. May 02 '17 at 23:48