@rkenmi - How to convert a PPM image to grayscale in C++

How to convert a PPM image to grayscale in C++


Convert a colored image to grayscale in C++ using the PPM image type and IfranView.

How to convert a PPM image to grayscale in C++


Back to Top

Updated on June 10, 2017

What is PPM?
PPM is simply an image format that is very easy to work with, for modifying purposes.

We'll create this image format from scratch, using C++.

PPM Images can be opened with IfranView.

Step 1 - The class for PPM image

First, let's define a class for this PPM image.

class PPMImage {  
    public:
        PPMImage(){;};
        ~PPMImage(){
            delete threeChan;
        }
        friend std::istream& operator >>(std::ifstream& inputStream, PPMImage& img);
        friend std::ostream& operator <<(std::ofstream& outputStream, const PPMImage& img);
        void grayscale();
            // feel free to add more if you want!

    private:
        string magicNumber; // A "magic number" for identifying the file type
        int width; // Width of the image
        int height; // Height of the image
        int maxColorVal; // Maximum color value
        char *threeChan; // A series of rows and columns (raster) that stores important binary image data
};

string magicNumber - For simplicity of this guide, we'll let this be "P6".

int width - Width (in pixels) of the image.

int height - Height (in pixels) of the image.

int maxColorVal - Maximum color value, which should be less than 65536 but greater than 0. For the simplicity of this guide, let this be 255.

char *threeChan - Each channel is represented in pure binary by 1 byte (since our maxColorVal is 255). Each channel of a pixel will only take up 1 byte. Each pixel is a triplet of red, green, and blue channels in that order.

Step 2 - Storing image data into object

The following code will extract all the important information from an original image, such as the magicNumber, width, height, maxColorVal, and of course, the RGB pixel data. We assume that the first parameter inputStream has already opened the source PPM image file.

std::istream& operator >>(std::ifstream& inputStream, PPMImage& img){  
    string fileName;
    char dump;

    inputStream >> img.magicNumber;

    string myString;
    inputStream >> myString;
    img.width = atoi(myString.c_str());

    inputStream >> myString;
    img.height = atoi(myString.c_str());

    inputStream >> myString;
    img.maxColorVal = atoi(myString.c_str());

    int memoryBlockSize = img.width * img.height * 3; //This means the size is 3 bytes

    img.threeChan = new char[memoryBlockSize];

    inputStream.read(&dump, 1); // ignore newline
    inputStream.read(img.threeChan, img.width * img.height * 3); //Read data into array
}

Step 3 - The grayscale function

The way the grayscale function works is simple enough. The important formula/equation is the following:

newRed  = (oldRed * 0.299) + (oldGreen * 0.587) + (oldBlue * 0.114);  
newGreen = (oldRed * 0.299) + (oldGreen * 0.587) + (oldBlue * 0.114);  
newBlue = (oldRed * 0.299) + (oldGreen * 0.587) + (oldBlue * 0.114);  

We simply modify the current binary image data by using the formulas above.

We use unsigned char for the RGB values instead of char

void PPMImage::grayscale(){  
    char *init, *reset;
    unsigned char oldRed, oldGreen, oldBlue, newRed, newGreen, newBlue;

    reset = threeChan; // keep track of initial pointer position

    for(int i = 0; i < width * height; i++){
        init = threeChan;

        oldRed = *threeChan;
        threeChan++;
        oldGreen = *threeChan;
        threeChan++;
        oldBlue = *threeChan;

        newRed  = (oldRed * 0.299) + (oldGreen * 0.587) + (oldBlue * 0.114);
        newGreen = (oldRed * 0.299) + (oldGreen * 0.587) + (oldBlue * 0.114);
        newBlue = (oldRed * 0.299) + (oldGreen * 0.587) + (oldBlue * 0.114);

        threeChan = init;
        *threeChan = newRed;
        threeChan++;
        *threeChan = newGreen;
        threeChan++;
        *threeChan = newBlue;
        threeChan++;
    }
    threeChan = reset; // reset pointer position
}

Step 4 - The write/save function

Here is an overloaded left-shift operator function that takes a PPMImage parameter and sends the image data into the outputStream. Again, we assume that the first parameter outputStream has already opened the destination PPM image file.

std::ostream& operator <<(std::ofstream& outputStream, const PPMImage& img){  
    int memoryBlockSize = img.width * img.height * 3;
    outputStream << img.magicNumber << '\n' << img.width << ' ' << img.height << '\n' << img.maxColorVal << '\n';
    outputStream.write(img.threeChan, memoryBlockSize);
}

Step 5 - Test Drive

Now is the time to test things out in main().

int main(){  
        PPMImage p;
        ifstream inStream;
        ofstream outStream;
        string srcFileName, outFileName;

        cout << "Please specify the source PPM filename: ";
        cin >> srcFileName;
        inStream.open(srcFileName, std::ifstream::binary);
        inStream >> p;
        inStream.close();
        cout << "Please specify the output PPM filename: ";
        cin >> outFileName;
        p.grayscale();
        outStream.open(outFileName, std::ifstream::binary);
        outStream << p;
        outStream.close();
        cout <<  "The " << srcFileName << " has been converted to grayscale and saved as " << outFileName << endl;
}

Output

Please specify the source PPM filename: rawr.ppm
Please specify the output PPM filename: rawrGray.ppm
The rawr.ppm has been converted to grayscale and saved as rawrGray.ppm

Download rawr.ppm

Try converting this file into grayscale!
You may want to use IfranView (or other image viewers) and check to see if it works!


Article Tags:
C++ppmgrayscalemultimedia