Hi programmers, I am a c# programmer but I have a very big project in C++ (Which I am not familiar with) and I am facing a big problem.
As part of the project, I need to convert the image (any image of any type bmp,jpeg,png...etc) to grayscale using openMP.
After doing some research, I got the main method which allows me to do the conversion using openMP, it's as follows:

// pDest is an unsigned char array of size width * height
// pSrc is an unsigned char array of size width * height * 4 (32-bit) 
// To avoid floating point operation, all floating point weights 
// in the original grayscale formula
// has been changed to integer approximation 

// Use pragma for to make a parallel for loop 
omp_set_num_threads(threads); 

#pragma omp parallel for
for(int z = 0; z < height*width; z++) 
{ 
    pDest[z] = (pSrc[z*4+0]*3735 + pSrc[z*4 + 1]*19234+ pSrc[z*4+ 2]*9797)>>15; 
}

But the problem is that I couldn't find any complete code to do that! So I am facing hard times reading the image first before processing it and later save it somewhere on the hard disk.

I would appreciate any help, even if the code is in serial (not parallel) I am sure it will help me a lot.

Recommended Answers

All 21 Replies

What exactly is it that you don't know how to do? Looks like each pSrc points to the image to change, with each pixel taking 4 bytes (I'd guess RGBA), and you have to read those four numbers and turn them into one new number in the greyscale image.

Looks like you've even decided already how to do that conversion - pDest[z] = (pSrc[z*4+0]*3735 + pSrc[z*4 + 1]*19234+ pSrc[z*4+ 2]*9797)>>15; - so what is it that you don't know how to do?

Moschops.. thanks for the reply.
What I don't know are the basic stuff, like what to include, how to read the image from hard disk and how to save it.
In other words, I have the function above, what are the steps needed to call this function.

Thanks in advance

Pick an imaging library that gives you access to pixel data. CImg is easy to use. Here's simple code that reads pixel values in an image. You could adapt this easily to load up an array and point your pSrc at the front.

#include "CImg.h"
#include <iostream>
using namespace cimg_library;

int main()
{
  CImg<float> image("hills.png");
  CImgDisplay main_disp(image);
  float pixvalR = image(10,10,0,0); // read red val at coord 10,10
  float pixvalG = image(10,10,0,1); // read green val at coord 10,10
  float pixvalB = image(10,10,0,2); // read blue val at coord 10,10
  std::cout << "R = " << pixvalR << ", G = " << pixvalG << ", B = " << pixvalB;

  std::cin.ignore();
}

Moschops.. thanks a lot for the help. I will check this today and if it is solved by applying openMP, I will check it as solved

Moschops, I downloaded the Cimg.h library, and I added the file to the header files section and added a new file in the source files and copied the code you sent me, but when I built the program it gave me an error:

Error   1   error C1083: Cannot open include file: 'CImg.h': No such file or directory

I tried to add all the header files that came with the Cimg library but still getting the same error.
Please tell me what should I do to fix this error.

Thanks a lot in advance

You should put the CImg.h header file somewhere the preprocessor can find it. Do you know what a header file is? Do you know what they are for? In a related question, who is asking you to use openMP without having taught you about header files?

I told you I am a senior c# developer but I have no idea of c++. I started 2 days ago.
I have an idea what are the header files and where to put them, and I fixed the error that I told you about, I should include the path in the project properties, include directories.
I am taking a parallel programming course and they use c++. I know it's stupid to teach us something that we don't have rigid background about, but this is the course syllabus.

Thanks a lot for the link. Now my project builds. But when I am getting an error when I reach this section:

CImg<float> image("image.png");

I tried to give it the physical path which is d:\image.png but didn't work. I added the image in the resources Files section, but still it's giving me an error.
I did some debug, and reached the following results:

When it's reaching this part:

else if (!cimg::strcasecmp(ext,"png")) load_png(filename);

ext value is 0x00492fb6 "png"
but I can't see it going to the load_png(filename) method. Instead, it's continuing in the else section until it reaches this part:

file = cimg::fopen(filename,"rb");

filename value: 0x00492fb0 image.png

and here the following error occurs:
"First-chance exception at 0x00CD3147 in MyProject.exe: 0xC00000FD: Stack overflow (parameters: 0x00000000, 0x00272000).

If there is a handler for this exception, the program may be safely continued."

I tried to give it the physical path which is d:\image.png but didn't work.

Double check that the file is definitely called image.png and is at the root of your D: drive, and then try d:/image.png (i.e. / instead of \ )

Failing that, it's worth experimenting with other image formats and also with putting the image directly into the working directory of the executable when you run it, so it doesn't rely on directory paths. The code I gave above works perfectly on every machine I've ever tried it on with the image sitting next to the executable.

This is my first experience in C++ and it doesn't look good at all even though I am doing something simple and would take me 2 minutes in c#.
Moschops, I really appreciate your help, but what do you think I should do to overcome this error? Since yesterday I have been trying all the possible ways, I tried to read the image as :
d:\image.png and d:/image.png and I put it in the resource files folder in the project and I put it in the same directory were my project is saved and in the Debug folder beside the executable as you told me to do, and still getting the same error. :S
I even tried to use something called MagicK++ and I am also having hard time with it.
Can you please send me a .rar file having the solution in it? I know it's the same but.. I really don't know what to do and I need it urgently to move on with my project

Are you using Visual Studio or some other IDE abomination? I'm guessing you are becuase you meantion "resource files folder" and I've got no idea what that means and the code above has nothig to do with it. Sometimes the working directory is not the same as the directory the executable is in. I freely admit that this would take me two minutes using a text editor and a command line but a lot longer using an IDE like Visual Studio.

I think you should try to open a file. Not an image file. Just a file. A text file. Check to see if you can open and read a simple text file.

walid.........
Try this:

#pragma omp parallel for
for (i=0; i < numPixels; i++)
{
 pGrayScaleBitmap[i] = (unsigned BYTE)
                       (pRGBBitmap[i].red * 0.299 +
                        pRGBBitmap[i].green * 0.587 +
                        pRGBBitmap[i].blue * 0.114);
}

Moschops.. guess what??!! It worked. Yes I am using Visual Studio 2012.
All what I have done is created a new empty project, put your code and added the Cimg.h in the include directories and everything worked perfectly.
Now, I want to use the code that Ziad provided me to convert to grayscale. So how can I know the numPixels of the image? And when the array pGrayScaleBitmap is filled, is there a way to convert it back to bmp and save it to hard disk?
Thanks in advance

Obvious though it sounds, learning how to read library documentation is a fundamental skill.

So how can I know the numPixels of the image?

In my code above, image is an object of type CImg, made using the CImg constructor. So, how to ask an object of type CImg how many pixels it contains?

http://cimg.sourceforge.net/reference/structcimg__library_1_1CImg.html

Here is the CImg class reference. Look through the member functions. Aha. There is one called width and one called height. If you know the width and height of an image you can calculate how many pixels it contains.

http://cimg.sourceforge.net/reference/structcimg__library_1_1CImg.html#a369399896761e31ae71db57fdd0ba431

So, the width member function returns an int. It will be called as follows:

int widthOfImage;
widthOfImage = theCImgObject.width();

Similarly for height.

If you go looking through the documentation and the examples, you will see ways to create a CImg object from scratch, and save it to disk.

Alternatively, you could be a bit smarter and realise that you don't actually need another CImg object. You could simply overwrite each pixel of the one you already have and then save that CImg object to disk as a new image. Once you look over the documentation and see what functions and classes are available, you'll think of ways to use them.

Is C++ that complicated??! I got the numPixel now as you told me, but if I want to use the function:

#pragma omp parallel for
    for (i=0; i < numPixels; i++)
    {
    pGrayScaleBitmap[i] = (unsigned BYTE)
    (image[i].red * 0.299 +
    image[i].green * 0.587 +
    image[i].blue * 0.114);
    }

I first need to declare array of unsigned BYTE pGrayScaleBitmap, but how ? google for the first time is not helping me. Plus, I don't know the size of this array, can I give it dynamic size?
Then I tried

image[i].red * 0.299

but visual studio is giving me red line under image. If it's wrong, how can I read the image RGB values?

I also found this code to display the image, I tested it alone and it's working:

grayImage.display("Grayscale Image");

Moschops, I know you might be laughing at how simple all this is, but this is driving me crazy and I am really thankful for your help especially that tomorrow is the due date.

I'm not laughing, I assure you. There's nothing funny about this.

I first need to declare array of unsigned BYTE pGrayScaleBitmap

Here is how to decalre an array of BYTE named pGrayScaleBitmap.

BYTE pGrayScaleBitmap[sizeOfArray];

I don't know the size of this array

It's the number of pixels in the image.

visual studio is giving me red line under image

This is why I hate IDEs. They're dangerous for beginners. They hide things from you and when it goes wrong, you've got no idea why. Does the object image actually exist? Is it an array or some other object that the operator [] makes sense for?

For sure yes because I got the width and height from it.
The error is: "Expression must have class type".

I also tried to give the array the numOfPixels but it gave me this error:
"Expression must have a constant value"

I tried:

int numOfPixels = 0;
numOfPixels = width * height

it didn't work too.

I think I was mistaken in my last post, image is not an object, I got confused by the names.
I read a bit about pointers and structures and now I have my code as follows:

#include "CImg.h"
    #include <iostream>
    #include <omp.h>
    #include <string>

    using namespace cimg_library;
    struct imgOb
    {
      float red;
      float green;
      float blue;
      imgOb(int x, int y)
        {
            red = image(x,y,0,0);
            green = image(x,y,0,1);
            blue = image(x,y,0,2);
        }        
    };

    int main()
    {
        CImg<float> image("D:\image.png");
        CImgDisplay main_disp(image);
        CImg<float> grayImage(image);

        //Image Width and Height
        int widthOfImage;
        widthOfImage = image.width();
        int heightOfImage;
        heightOfImage = image.height();

        //Get Image Number of Pixels
        int numOfPixels = widthOfImage * heightOfImage;

        BYTE pGrayScaleBitmap[1];
        BYTE *B;
        B = new BYTE[numOfPixels];
        int counter = 0;
        imgOb *iob;

    #pragma omp parallel for
        for ( int i = 0; i <= heightOfImage; i++)
        {
            for (int j = 0; j <= widthOfImage; j++)
            {
                imgOb::imgOb(i,j);
                B[i] = (BYTE)
                    (   
                    iob[i].red * 0.299 +
                    iob[i].green * 0.587 +
                    iob[i].blue * 0.114);
            }
        }

        //removes pointers to save memory
        delete B;
        delete iob;

        //Show Image
        grayImage.display("Grayscale Image");   

        // Save image to hard disk
    }

This is giving me an error when j reaches 213 (Access violation reading location 0x0091D1A0).
What am I doing wrong here? Is there a better (easier) way?

At last, I figured out how to do the conversion, how to save (even though not jpeg, but I wanted bin file anyways) and how to display the original and final image. I will post the code for anyone who might need it or give a better solution. Special thanks for Moschops.

#include <omp.h>
#include <iostream>
#include <string>
#include "CImg.h"

using namespace std;
using namespace cimg_library;

CImg<float> image("D:\lena.jpg");
CImg<float> grayImage(image);

struct imgOb
{
    imgOb(int x, int y)
    {
        grayImage(x,y) = (image(x,y,0,0) * 0.299) + (image(x,y,0,1) * 0.587) + (image(x,y,0,2) * 0.114);
    }
};

int main()
{
    //Image Width and Height
    int widthOfImage;
    widthOfImage = image.width();
    int heightOfImage;
    heightOfImage = image.height();

    //Get Image Number of Pixels
    int numOfPixels = widthOfImage * heightOfImage;

    BYTE pGrayScaleBitmap[1];
    BYTE *B;
    B = new BYTE[numOfPixels];
    int counter = 0;
    imgOb *iob;
    iob = new imgOb(0,0);

    //declare timer
    double timer = 0.0;
    timer = omp_get_wtime();

    omp_set_num_threads(3);
    #pragma omp parallel for
    for ( int i = 0; i <= widthOfImage; i++)
        {
            #pragma omp parallel for
            for (int j = 0; j <= heightOfImage; j++)
            {
                imgOb::imgOb(i,j);
            }
        }

    //Shows the time conversion took
    int threadNum = omp_get_max_threads();
    cout << "Threads used = " << threadNum << endl;
    cout << "Conversion time is= " << 1000*(omp_get_wtime() - timer) << endl;

    //removes pointers to save memory
    delete B;
    delete iob;

    //Save image to hard disk
    grayImage.save("D:\OutputGrayScale",1);

    //Show Image
    CImgDisplay main_disp(image);
    grayImage.display("Grayscale Image");
}

The problem with this:
for (int j = 0; j <= heightOfImage; j++)
is that you are letting j go too high.

If the image is of height 5, for example, then the rows are numbered 0,1,2,3,4.

Your code starts at row 0, and then goes through 1,2,3,4,5. Trying to work on data that does not exist; the memory location exists, so you're trashing some other data in memory. This is bad.

To summarise; in C++, arrays of size n go from array[0] to array[n-1]. There is no array[n]. That is too far.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.