Search code examples
c++algorithmscalebmp

How to Scale a .bmp Image using C++ and just the iostream and stdio headers?


I am currently doing my first programming assignment in C++, and my objective is to Scale a Bitmap image using just the basic IO headers.

I've used http://en.wikipedia.org/wiki/BMP_file_format#DIB_header_.28bitmap_information_header.29 as my reference guide to finding the Header information.

My problem is creating an algorithm to scale the image up in terms of adding in individual pixels. I have made a series of for loops which are commented (They use the header information to determine how much they need to run), but I have no idea what to do next in terms of manipulating the actual pixels, this is where I need help

When I run the program, a new BMP file (called Image2) is created, however it is corrupt and 1kb in size (Most likely because the program isn't finished).

Here is the code, if anyone can also critique me on this or let me know any bad practices to avoid that I'm currently doing I would appreciate it.

 int main()
    {
    
    FILE* pFile = NULL;
    FILE* pFile2 = NULL;
    
        
        //Open Image.bmp (In directory) as read binary
        pFile = fopen("Image.bmp", "rb");
        //Scan through the program and set to send.
        fseek (pFile, 0, SEEK_END);
        //Read file data
        int fileData = ftell (pFile);
        //set back to start of char pointer
        rewind (pFile);
    
    
        //Create a Char Filebuffer, this will be where data is viewed.
        char* FileBuffer = new char[fileData];
        //Read pFile into Filebuffer
        fread(FileBuffer, fileData, 1, pFile);
    
            //Read out BMP Header Data and Cast to Int
            int ImageSize = *(int*)&FileBuffer[2];
            int ImageOffset = *(int*)&FileBuffer[10]; //ImageOffset = Image Header Size
            int ImageHeight = *(int*)&FileBuffer[18];
            int ImageWidth = *(int*)&FileBuffer[20];
            int BitsPerPixel = *(int*)&FileBuffer[24];
    
        //Create a New Buffer that starts off at Pixel Data
        char* NewFileBuffer = FileBuffer + ImageOffset;
    
            
                std::cout << "Enter the amount of times you want to scale the image by." << std::endl;
                std::cin >> ScaleValue;
                std::cout << "\n";
    
                //Create New Image row/height/size variables
                int RowSizeOld = (BitsPerPixel * ImageWidth +31)/32 * 4;
                int RowSizeNew = (BitsPerPixel *(ImageWidth * ScaleValue) + 31) / 32 * 4;
                int NewImageSize = ImageOffset + (RowSizeNew * (ScaleValue * ImageHeight) );
                int NewImageHeight = ImageHeight * ScaleValue;
                int NewImageWidth = ImageWidth * ScaleValue;
    
    
    // These ints define the colour values for RGB and Padding
    int r = ImageOffset + 1; 
    int g = ImageOffset + 2; 
    int b= ImageOffset + 3; 
    int p = ImageOffset + 4; 
    
    
    
    //Rescale Image here, for (Newfile buffer [0])
        // This for loop figures out how many times the newHeight variable must iterate
        for (int newHeight = 0 ; newHeight < NewImageHeight ; newHeight++)
        {
            //This line scales up the amount of rows in terms of scale value
            for(int doubleLine = 0 ; doubleLine < ScaleValue ; doubleLine++)
            {
                //This for loop then figures out the width of the new image in pixels as an int value
                for (int newWidth = 0 ; newWidth < NewImageWidth ; newWidth++)
                { 
                    //This loop figures out how many times you need to increase the pixel density (for each pixel)
                    for (int pixelMultiplier = 0 ; pixelMultiplier < ScaleValue ; pixelMultiplier++)
                    {
                    
                  //Move pixel data around to scale image (This is where I'm having trouble)
        
                    }
                }
    
    
            }
    
    
        }
    
    
        //Create a new File pointer, add image name + bmp string
        FILE* pFile2;
        pFile2 = fopen("Image2.bmp", "wb");
        fwrite(NewFileBuffer, (NewImageSize + ImageOffset), sizeof(char) , pFile2);
    
    return 0;
    }

This is my first post on SE, so my apologies if the format of the code is a bit chunky or the question is hard to understand (I've read through the forum and have trimmed my code to make it more readable)


Solution

  • Doing a simple nearest re-sample is probably the easiest way to do it. To do this you simply calculate a ratio between the new and old sizes as follows:

    float xStep = (float)oldWidth / (float)newWidth;
    float yStep = (float)oldHeight / (float)newHeight;
    

    Now you know that as you step 1 pixel in the x direction for the new image you would step "xStep" pixels in the old image. You then round the sampling position to the nearest integer location and sample that from the old image and write it to the new! You now have a simple re-sampled image.

    For even better results you can Bi-linear filter as you re-sample. This is a very simple 2D linear interpolation.

    To give you a 1D example of filtering float values you would write some code something like as follows:

    for( float x = 0, int xOut = 0; x += xStep, xOut++; xOut < newWidth; )
    {
        const float sample0  = old1D[std::floor( x )];
        const float sample1  = old1D[std::ceil( x )];
    
        const float t        = x - std::floor( x );
    
        const float sample   = sample0 + ((sample1 - sample0) * t);
    
        new1D[xOut] = sample;
    }
    

    Of course I'm not taking care of edge cases, but taking care of those and extending to 2D ought to be left as an exercise in the case of an assignment.

    Its also worth noting that this sort of filtering will look a bit wrong if you scale the image more than 2x downwards due to the fact that you still step over pixels completely. This is usually solved by pre-filtering the image into a set of images of half the width and height at each step. This is known as mip-mapping.

    Good luck!