I have an image (frame.bmp, uncompressed, resolution 640*480, 3bit depth, 900KB size) stored at C:\Frame.bmp. I want to load it and put it in my program as an array a[N][M], N=479, M=639. After that to modify its elements (e.g thresholding) and create a new array B[479][639]. Finally, to store it in the path as C:\Frame2.bmp and display the new image, lets say with paint application.

My program is:

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

   unsigned int** bufalloc(int N,int M);
   int threshold(unsigned int** b,unsigned int** a,unsigned int T,int N,int M);
   int fromdisk(char* imfile,unsigned int** a,int N,int M);
   int todisk(char* imfile,unsigned int** a,int N,int M);
   unsigned int** af;
   unsigned int** bf;

 int main(){
   int f1,f2,f3; 
   af = bufalloc(479,679);
     if(af==NULL) return(-100);
   bf = bufalloc(479,1024);
     if(bf==NULL) return(-100);
   f1 = fromdisk("F.bmp",af,479, 679);
   f2 = threshold(bf,af,40, 479, 679);
   f3 = todisk("tF.bmp",bf, 479, 679);
   return(0);
}
///////////////////////////////////////////////////////////////
int fromdisk(char* imfile,unsigned int** a,int N,int M){
   int i,j;
   unsigned int* buc;
   FILE* fp;
      if (a==NULL) return(-1);
      if (N<0 || M<0) return(-21);
   fp = fopen(imfile, "rb");
      if (fp==NULL) return(-300);
   buc = (unsigned int*)malloc(M*sizeof(unsigned int));
      if(buc==NULL) return(-10);
   for(i=0; i<N; i++){
      fread(buc,sizeof(unsigned int),M,fp);
      for(j=0; j<M; j++)
            a[i][j] = buc[j];
   }
   free(buc);
   fclose(fp);
   return(0);
}
///////////////////////////////////////////////////////////
int todisk(char* imfile,unsigned int** a,int N,int M){
   int k,l;
   unsigned int* buc;
   FILE* fp;
   fp = fopen(imfile, "wb");
      if (fp==NULL) return(-300);
   buc = (unsigned int*)malloc(M*sizeof(unsigned int));
      if(buc==NULL) return(-10);
        for(k=0; k<N; k++){
	   for(l=0; l<M; l++)
  		  buc[l] = a[k][l];
  	   fwrite(buc,sizeof(unsigned int),M,fp);
   }
   free(buc);
   fclose(fp);
   return(0);
}
/////////////////////////////////////////////////////////////////////////////////////
int threshold(unsigned int** b,unsigned int** a,unsigned int T,int N,int M){
   int i,j;
   for(i=0; i<N; i++){
      for(j=0; j<M; j++){
         if (a[i][j]<T)
            b[i][j]=0;
         else b[i][j]=1;
	  } 
   }
   return(0);
}
//////////////////////////////////////////
unsigned int** bufalloc(int N,int M){
   unsigned int** m;
   int r;
   m = (unsigned int**)malloc(N*sizeof(unsigned int*));
   for(r=0; r<N; r++){
      m[r] = (unsigned int*)malloc((M*sizeof(unsigned int)));
      if(m[r]==NULL)		
         return(NULL);
   }
   return(m);
}

But, it does not work corectly (the new image is now 991KB and cannot open)! What happens with the header? Any idea how it can work?

so... is this work for you?

#include <iostream>
#include <fstream>
using namespace std;

struct color {
	unsigned char blue;
	unsigned char green;
	unsigned char red;
};

struct image {
	char header[54];
	color data[480][640];
};

void threshold(image& Frame);

int main(int argc, char **argv) {
	ifstream input_image;
	input_image.open("C:/Frame.bmp");
	if (!input_image.is_open()) {
		cout << "Error opening C:\\Frame.bmp" << endl;
		return -1;
	}
	image Frame;
	input_image.read((char*) &Frame, sizeof(Frame));

	threshold(Frame);

	ofstream output_image;
	output_image.open("C:/Frame2.bmp");
	if (!output_image.is_open()) {
		cout << "Error opening C:\\Frame2.bmp" << endl;
		return -1;
	}
	output_image.write((char*) &Frame, sizeof(Frame));
	return 0;
}

void threshold(image& Frame) {
	for (int i = 0; i < 480; ++i) {
		for (int j = 0; j < 640; ++j) {
			if (Frame.data[i][j].blue < 40)
				Frame.data[i][j].blue = 0;
			else
				Frame.data[i][j].blue = 0xFF;
			if (Frame.data[i][j].green < 40)
				Frame.data[i][j].green = 0;
			else
				Frame.data[i][j].green = 0xFF;
			if (Frame.data[i][j].red < 40)
				Frame.data[i][j].red = 0;
			else
				Frame.data[i][j].red = 0xFF;
		}
	}
}

I know this article has been dead for years but this is exactly what i was looking for. i don't mean the treshold function, i mean the code to put a bmp into a 2-d array.

The solution given by ivailosp works fine with brand new bmp files (created with paint for example) but i can't understand why it gives problem with converted files.
With such files the code keep working, it creates Frame2.bmp but the resulting image is deformed and with different colors.

Does anybody have an idea?

@Galperin the above code is horrible. It will also only work for 32-bit bitmaps. It does not handle 24-bit bitmaps which have padding. For that reason, you're going to get a deformed image.

The below code will work:

#include <iostream>
#include <fstream>

typedef struct
{
    uint8_t r, g, b, a;
} rgb32;


#pragma pack(2)
typedef struct
{
   uint16_t bfType;
   uint32_t bfSize;
   uint16_t bfReserved1;
   uint16_t bfReserved2;
   uint32_t bfOffBits;
} bitmap_file_header;
#pragma pack()


#pragma pack(2)
typedef struct
{
    uint32_t biSize;
    int32_t biWidth;
    int32_t biHeight;
    uint16_t biPlanes;
    uint16_t biBitCount;
    uint32_t biCompression;
    uint32_t biSizeImage;
    int16_t biXPelsPerMeter;
    int16_t biYPelsPerMeter;
    uint32_t biClrUsed;
    uint32_t biClrImportant;
} bitmap_info_header;
#pragma pack()


#pragma pack(2)
typedef struct
{
    bitmap_file_header fHeader;
    bitmap_info_header iHeader;
} bitmap_header;
#pragma pack()



class bitmap
{
private:
    bitmap_header header;
    uint8_t* pixels;

public:
    bitmap(const char* path);
    ~bitmap();

    void save(const char* path);

    rgb32* getPixel(uint32_t x, uint32_t y) const;
    void setPixel(rgb32* pixel, uint32_t x, uint32_t y);

    uint32_t getWidth() const;
    uint32_t getHeight() const;
};

bitmap::bitmap(const char* path) : header(), pixels(nullptr)
{
    std::ifstream file(path, std::ios::in | std::ios::binary);

    if (file)
    {
        file.read(reinterpret_cast<char*>(&header.fHeader), sizeof(header.fHeader));

        if (header.fHeader.bfType != 0x4d42)
        {
            throw std::runtime_error("Invalid format. Only bitmaps are supported.");
        }

        file.read(reinterpret_cast<char*>(&header.iHeader), sizeof(header.iHeader));

        if (header.iHeader.biCompression != 0)
        {
            throw std::runtime_error("Invalid bitmap. Only uncompressed bitmaps are supported.");
        }

        if (header.iHeader.biBitCount != 24)
        {
            throw std::runtime_error("Invalid bitmap. Only 24bit bitmaps are supported.");
        }

        file.seekg(header.fHeader.bfOffBits, std::ios::beg);

        pixels = new uint8_t[header.fHeader.bfSize - header.fHeader.bfOffBits];
        file.read(reinterpret_cast<char*>(&pixels[0]), header.fHeader.bfSize - header.fHeader.bfOffBits);


        uint8_t* temp = new uint8_t[header.iHeader.biWidth * header.iHeader.biHeight * sizeof(rgb32)];

        uint8_t* in = pixels;
        rgb32* out = reinterpret_cast<rgb32*>(temp);
        int padding = (header.iHeader.biSizeImage - header.iHeader.biWidth * header.iHeader.biHeight * 3) / header.iHeader.biHeight;

        for (int i = 0; i < header.iHeader.biHeight; ++i, in += padding)
        {
            for (int j = 0; j < header.iHeader.biWidth; ++j)
            {
                out->b = *(in++);
                out->g = *(in++);
                out->r = *(in++);
                out->a = 0xFF;
                ++out;
            }
        }

        delete[] pixels;
        pixels = temp;
    }
}

bitmap::~bitmap()
{
    delete[] pixels;
}

void bitmap::save(const char* path)
{
    std::ofstream file(path, std::ios::out | std::ios::binary);

    if (file)
    {
        file.write(reinterpret_cast<char*>(&header.fHeader), sizeof(header.fHeader));
        file.write(reinterpret_cast<char*>(&header.iHeader), sizeof(header.iHeader));
        file.seekp(header.fHeader.bfOffBits, std::ios::beg);


        uint8_t* temp = new uint8_t[header.fHeader.bfSize - header.fHeader.bfOffBits];

        uint8_t* out = temp;
        rgb32* in = reinterpret_cast<rgb32*>(pixels);
        int padding = (header.iHeader.biSizeImage - header.iHeader.biWidth * header.iHeader.biHeight * 3) / header.iHeader.biHeight;

        for (int i = 0; i < header.iHeader.biHeight; ++i, out += padding)
        {
            for (int j = 0; j < header.iHeader.biWidth; ++j)
            {
                *(out++) = in->b;
                *(out++) = in->g;
                *(out++) = in->r;
                ++in;
            }
        }

        file.write(reinterpret_cast<char*>(&temp[0]), header.fHeader.bfSize - header.fHeader.bfOffBits);
        delete[] temp;
    }
}

rgb32* bitmap::getPixel(uint32_t x, uint32_t y) const
{
    rgb32* temp = reinterpret_cast<rgb32*>(pixels);
    return &temp[(header.iHeader.biHeight - 1 - y) * header.iHeader.biWidth + x];
}

void bitmap::setPixel(rgb32* pixel, uint32_t x, uint32_t y)
{
    rgb32* temp = reinterpret_cast<rgb32*>(pixels);
    memcpy(&temp[(header.iHeader.biHeight - 1 - y) * header.iHeader.biWidth + x], pixel, sizeof(rgb32));
};

uint32_t bitmap::getWidth() const
{
    return header.iHeader.biWidth;
}

uint32_t bitmap::getHeight() const
{
    return header.iHeader.biHeight;
}

int main()
{
    bitmap bmp{"C:/Users/Brandon/Desktop/Test.bmp"};

    for (int i = 0; i < bmp.getWidth(); ++i)
    {
        rgb32 pixel = {0x0, 0, 0xff, 0};
        bmp.setPixel(&pixel, i, 0);
    }

    bmp.save("C:/Users/Brandon/Desktop/TestCopy.bmp");
}

Edited 1 Year Ago by triumphost

This article has been dead for over six months. Start a new discussion instead.