Thread: Write array to to BMP

  1. #1
    Registered User
    Join Date
    Feb 2013
    Posts
    17

    Write array to to BMP

    I have been learning about the .bmp format an am able to read in an 8bpp image into an array. Now I want to know how to take an array ex: Matrix[i][j] and print to bmp file. This is what I have so far,

    Code:
    /////////////// write image to disk
    void writeBMP(unsigned char **Matrix, int Matrix_dimension){
        FILE *out;
        int ii,jj;
    
    
        int filesize = 54 + 4*256 + Matrix_dimension*Matrix_dimension;
    
    
        unsigned char bmpfileheader[14] = {'B','M', 0,0,0,0, 0,0,0,0, 54,0,0,0};
        unsigned char bmpinfoheader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 8,0};
        unsigned char bmpcolourtable[1024];
    
    
        bmpfileheader[ 2] = (unsigned char)(filesize    );
        bmpfileheader[ 3] = (unsigned char)(filesize>> 8);
        bmpfileheader[ 4] = (unsigned char)(filesize>>16);
        bmpfileheader[ 5] = (unsigned char)(filesize>>24);
    
    
        bmpinfoheader[ 4] = (unsigned char)(Matrix_dimension    );
        bmpinfoheader[ 5] = (unsigned char)(Matrix_dimension>> 8);
        bmpinfoheader[ 6] = (unsigned char)(Matrix_dimension>>16);
        bmpinfoheader[ 7] = (unsigned char)(Matrix_dimension>>24);
        bmpinfoheader[ 8] = (unsigned char)(Matrix_dimension    );
        bmpinfoheader[ 9] = (unsigned char)(Matrix_dimension>> 8);
        bmpinfoheader[10] = (unsigned char)(Matrix_dimension>>16);
        bmpinfoheader[11] = (unsigned char)(Matrix_dimension>>24);
    
    
        bmpcolourtable?????
    
    
    
    
    
    
        out = fopen("output.bmp","wb");
        fwrite(bmpfileheader,1,14,out);
        fwrite(bmpinfoheader,1,40,out);
        fwrite(bmpcolourtable,4,256,out);
    
    
        for(i=0;i<info.height;i++){
            for(j=0;j<info.width;j++){
                pos+= 1;
                fseek(out,pos,0);
                tmp = Matrix[i][j];
                fwrite(&tmp,(sizeof(unsigned char)),1,out);
            }
        }
        fflush(out);
        fclose(out);
    }

    I have a basic understanding of the format but dont know how to write the header and colour table form scratch.
    Thanks
    Last edited by bnickerson; 06-04-2013 at 02:06 PM.

  2. #2
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    As far as I remember there are padding bytes for each row to make the width divisible by 4
    Kurt

  3. #3
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Great info on the .bmp format here: BMP file format - Wikipedia, the free encyclopedia. If you need more specific help, ask a more specific question.

  4. #4
    Registered User
    Join Date
    Feb 2013
    Posts
    17
    OK, so I have the following code which can write a bmp provided you have read a bmp earlier (i.e copying the header info)

    Code:
    void writeBMP(unsigned char **Matrix, HEADER head, INFOHEADER info, FILE* arq){
    	FILE* out;
    	int i,j;
    	unsigned char tmp;
    	long pos = 1077;
    
    
    	char header[54];
    	char colourtable[1024];
    
    
    	fseek(arq,0,0);
    	fread(header,54,1,arq); 		// read header 54 bytes
    
    
    	out = fopen("out.bmp","wb");
    
    
    	fseek(out,0,0);
    	fwrite(header,54,1,out); 		// write header to output
    
    
    	fseek(out,54,0);
    	fwrite(info.colourtable,1024,1,out); // write colour table to output
    
    
    	for(i=0;i<info.height;i++){
    		for(j=0;j<info.width;j++){
    			pos+= 1;
    			fseek(out,pos,0);
    			tmp = Matrix[i][j];
    			fwrite(&tmp,(sizeof(unsigned char)),1,out);
    		}
    	}
    	fflush(out);
    	fclose(out);
    The problem I need help with is: Given only one of the above 4 inputs namely unsigned char **Matrix how do you write the header info from scratch?

  5. #5
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    It would help if we knew what HEADER and INFOHEADER looked like, to know if this code is on the right track. Also, you need to give us specifics on what type of bitmap you are trying to create. There is not one universal, fixed format for a bitmap. There are several variations. Your choice of DIB header, color table, will determine how you write out the header info. Re-read the File Structure section of the article carefully.

    Also, how do you know that the header of the arq file is exactly 54 bytes?

    In the absence of those things, I can only say: Your write function is more or less correct -- at least in concept. Write the header, write the color table, write the pixel array.

    One glaring error:
    You never check the value of out to see if fopen succeeded. If it fails (is NULL), you should return immediately, with an error code. You can't continue. You also never check the return values of your fwrite calls. If fwrite is failing, no point in continuing. General advice: Always, always, always, for every program you ever write for the rest of your life, no matter how small the program, check the return values of your functions.

    More subtle error:
    You appear to be missing the & from in front of header and info.colourtable in your fwrite calls. They require a pointer to the start of the data. If you typedef'ed the pointer into HEADER and INFOHEADER, I recommend you don't do that. It causes this exact type of misunderstanding/confusion, and confusing code means more errors, and they are harder to find and fix.

    When writing a whole structure to disk, remember there are alignment issues. You can pack the structure or write elements individually. If you do individually, I recommend breaking it up into functions for each section of the file (header, DIB, color table, pixel array).

    There is no need to fseek after each write. Writing 54 bytes to the file moves the file pointer 54 bytes forward for you automatically. This eliminates the need for a pos variable.

    You should also use the sizeof operator, in case the size of header, colortable, etc ever changes (e.g. you change DIB format). That way you wont have to change your writeBMP function, it will magically work. For example
    Code:
    if (fwrite(header, sizeof(header), 1, 0) != 1) {
        perror("fwrite of BMP header failed");
        // return with error or exit program -- your call
    }
    You don't need the tmp variable, you can use Matrix[i][j] directly in your fwrite calls:
    Code:
    if (fwrite(&Matrix[i][j], sizeof(Matrix[i][j]), 1, out) != 1)

  6. #6
    Registered User
    Join Date
    Feb 2013
    Posts
    17
    See file-format-bmp for the format of BMP files. I wish to write 8 bpp images thus 256 and all images will be square i.e. height = width
    this is the code so far..

    Code:
    /////////////// write image to disk
    voidwriteBMP(unsignedchar**Matrix,intMatrix_dimension){
    FILE*out;
    intii,jj,pos;
    
    
    intfilesize=54+4*256+Matrix_dimension*Matrix_dimension;
    intimagesize=Matrix_dimension*Matrix_dimension;
    
    
    unsignedcharbmpfileheader[14]={'B','M',filesize,0,0,0,0,0,0,0,1078,0,0,0};
    unsignedcharbmpinfoheader[40]={40,0,0,0,Matrix_dimension,0,0,0,Matrix_dimension,0,0,0,1,0,8,0,0,0,0,0,imagesize,0,0,0,0,0,0,0,0,0,0,0,256,0,0,0,0,0,0,0};
    unsignedcharbmpcolourtable[1024];
    
    
    for(ii=0;ii<1024;ii++){
    bmpcolourtable[ii]=0;
    }
    
    
    jj=3;
    for(ii=0;ii<62;ii++){
    bmpcolourtable[jj+1]=ii+1;
    bmpcolourtable[jj+2]=ii+1;
    bmpcolourtable[jj+3]=ii+1;
    jj=jj+3;
    }
    
    
    out=fopen("output.bmp","wb");
    fwrite(bmpfileheader,14,1,out);
    fwrite(bmpinfoheader,40,1,out);
    fwrite(bmpcolourtable,1024,1,out);
    
    
    for(ii=0;ii<Matrix_dimension;ii++){
    for(jj=0;jj<Matrix_dimension;jj++){
    pos+=1;
    fseek(out,pos,0);
    fwrite(&Matrix[ii][jj],(sizeof(unsignedchar)),1,out);
    }
    }
    fflush(out);
    fclose(out);
    }
    I need help writing to binary... I dont know how to format the header elements although I think I know all that has to be written (see link above for order)


    signature = 'BM'
    filesize = 54+4*256+Matrix_dimension*Matrix_dimension
    reserved = 0
    datafoset = 1078
    size = 40
    width =
    Matrix_dimension
    height =
    Matrix_dimension
    planes = 1
    Bitcount = 8
    compression = 0
    imagesize =
    Matrix_dimension*Matrix_dimension
    Xppm = 0
    Yppm=0
    coloursused = 256
    colourimp = 0
    colourtable = 0000111022203330.......6363630
    Last edited by bnickerson; 06-05-2013 at 10:47 AM.

  7. #7
    Registered User
    Join Date
    Feb 2013
    Posts
    17
    By trial and error looking at the bmps in a hex editor I have gotten to this point,

    Code:
    /////////////// write image to disk
    voidwriteBMP(unsignedchar**Matrix,intMatrix_dimension){
    FILE*out;
    intii,jj;
    longpos=1077;
    
    
    unsignedcharfilesize=(unsignedchar)54+4*256+Matrix_dimension*Matrix_dimension;
    unsignedcharoffset=(unsignedchar)1078;
    unsignedcharimagesize=(unsignedchar)Matrix_dimension*Matrix_dimension;
    unsignedchardimension=(unsignedchar)Matrix_dimension;
    unsignedcharnumcolor=(unsignedchar)256;
    
    
    unsignedcharbmpfileheader[14]={'B','M',filesize,0,0,0,0,0,0,0,offset,0,0,0};
    unsignedcharbmpinfoheader[40]={40,0,0,0,dimension,0,0,0,dimension,0,0,0,1,0,8,0,0,0,0,0,imagesize,0,0,0,0,0,0,0,0,0,0,0,numcolor,0,0,0,0,0,0,0};
    unsignedcharbmpcolourtable[1024];
    
    
    for(ii=0;ii<1024;ii++){
    bmpcolourtable[ii]=0;
    }
    jj=3;
    for(ii=0;ii<255;ii++){
    bmpcolourtable[jj+1]=(unsignedchar)ii+1;
    bmpcolourtable[jj+2]=(unsignedchar)ii+1;
    bmpcolourtable[jj+3]=(unsignedchar)ii+1;
    jj=jj+4;
    }
    
    
    for(ii=0;ii<1024;ii++)printf("%i\n",bmpcolourtable[ii]);
    
    
    out=fopen("output.bmp","wb");
    fwrite(bmpfileheader,14,1,out);
    fwrite(bmpinfoheader,40,1,out);
    fwrite(bmpcolourtable,1024,1,out);
    
    
    
    
    for(ii=0;ii<Matrix_dimension;ii++){
    for(jj=0;jj<Matrix_dimension;jj++){
    pos+=1;
    fseek(out,pos,0);
    fwrite(&Matrix[ii][jj],(sizeof(unsignedchar)),1,out);
    }
    }
    
    
    fflush(out);
    fclose(out);
    }
    I am soo close!! I can not see a difference between in input and output files when I use this function, however the output file doesn't open. Any ideas?

  8. #8
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Please make sure you copy-paste your code as plain text, with proper indentation. As it stands, the code you posted is very difficult to read.

    It would help if we had more data about the problem. Say, a complete program as well as a small sample bitmap file for us to test with that demonstrates your problem. Also, you personally might not see a difference between the files (via, say a hex editor), but consider using a diff utility compare the input and output files. Something more reliable than the human eye.

  9. #9
    Registered User
    Join Date
    May 2012
    Posts
    1,066
    Code:
    unsigned char filesize = (unsigned char) 54 + 4 * 256 + Matrix_dimension * Matrix_dimension;
    unsigned char offset = (unsigned char) 1078;
    unsigned char imagesize = (unsigned char) Matrix_dimension * Matrix_dimension;
    unsigned char is able to hold values from 0 to 255.
    Why do you cast the values of the expressions? Are you trying to avoid compiler warnings?

    Bye, Andreas
    Last edited by AndiPersti; 06-06-2013 at 07:07 AM. Reason: better description for range

  10. #10
    Registered User
    Join Date
    Feb 2013
    Posts
    17
    Quote Originally Posted by AndiPersti View Post
    Code:
    unsigned char filesize = (unsigned char) 54 + 4 * 256 + Matrix_dimension * Matrix_dimension;
    unsigned char offset = (unsigned char) 1078;
    unsigned char imagesize = (unsigned char) Matrix_dimension * Matrix_dimension;
    unsigned char is able to hold values from 0 to 255.
    Why do you cast the values of the expressions? Are you trying to avoid compiler warnings?

    Bye, Andreas

    Yes I was getting warnings so I cast them.. I know this is the incorrect section. I need to know how to write the larger numbers to the bmp file in binary mode.. Everything other then this is working.

    Code:
    unsigned char imagesize = (unsignedchar) Matrix_dimension*Matrix_dimension;
    unsigned char dimension = (unsignedchar) Matrix_dimension;
    unsigned char numcolor = (unsignedchar) 256;
    

    is wrong.. I dont know how to format it. It is so close!! hahah if i go in the hex file and fix these 3 elements then the output file is fixed
    Last edited by bnickerson; 06-06-2013 at 08:49 AM.

  11. #11
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    If you look at the file format link you provided, it tells you how many bytes each part of the header must be. An unsigned char is (usually) 1 byte, so if you must output a 2-byte field, you can't fwrite a single unsigned char, you need a variable that is the right width. I recommend you #include <stdint.h> and use the fixed-width types uint8_t, uint16_t and uint32_t which are guaranteed to be 8, 16 and 32 bits (1, 2 and 4 bytes1) respectively. Thus imagesize, which is 4 bytes, would be declared as a uint32_t. Then, when you write it, you simply do
    Code:
    fwrite(&imagesize, sizeof(imagesize), 1, out)
    NOTE: The multi-byte values in a BMP file are stored in little-endian byte order. If you are on x86, this is the native byte order, and you don't need to do anything. If you are on a big-endian machine however, you will need to reverse the byte order before writing each value to disk.

    1 Technically, the number of bits in a byte is defined by CHAR_BIT and can be something other than 8, however, I am not aware of any systems with CHAR_BIT != 8.

  12. #12
    Registered User
    Join Date
    Feb 2013
    Posts
    17
    Quote Originally Posted by anduril462 View Post
    If you look at the file format link you provided, it tells you how many bytes each part of the header must be. An unsigned char is (usually) 1 byte, so if you must output a 2-byte field, you can't fwrite a single unsigned char, you need a variable that is the right width. I recommend you #include <stdint.h> and use the fixed-width types uint8_t, uint16_t and uint32_t which are guaranteed to be 8, 16 and 32 bits (1, 2 and 4 bytes1) respectively. Thus imagesize, which is 4 bytes, would be declared as a uint32_t. Then, when you write it, you simply do
    Code:
    fwrite(&imagesize, sizeof(imagesize), 1, out)
    NOTE: The multi-byte values in a BMP file are stored in little-endian byte order. If you are on x86, this is the native byte order, and you don't need to do anything. If you are on a big-endian machine however, you will need to reverse the byte order before writing each value to disk.

    1 Technically, the number of bits in a byte is defined by CHAR_BIT and can be something other than 8, however, I am not aware of any systems with CHAR_BIT != 8.




    THANK YOU!! I am still a work in progress but you have taught me several valuable lessons in this post!

    My final code for writing a array to a 8bpp (greyscale 256) bmp is as follows

    Code:
    /////////////// write image to disk
    void writeBMP(unsigned char **Matrix, int Matrix_dimension){
        FILE *out;
        int ii,jj;
        long pos = 1077;
    
    
        out = fopen("output.bmp","wb");
    
    
        // Image Signature
        unsigned char signature[2] = {'B','M'};
        fseek(out,0,0);
        fwrite(&signature,2,1,out);
    
    
        // Image file size
        uint32_t filesize = 54 + 4*256 + Matrix_dimension*Matrix_dimension;
        fseek(out,2,0);
        fwrite(&filesize,4,1,out);
    
    
        // Reserved
        uint32_t reserved = 0;
        fseek(out,6,0);
        fwrite(&reserved,4,1,out);
    
    
        // Offset
        uint32_t offset = 1078;
        fseek(out,10,0);
        fwrite(&offset,4,1,out);
    
    
       // Info header size
        uint32_t ihsize = 40;
        fseek(out,14,0);
        fwrite(&ihsize,4,1,out);
    
    
        // Image Width in pixels
        uint32_t width = (uint32_t) Matrix_dimension;
        fseek(out,18,0);
        fwrite(&width,4,1,out);
    
    
        // Image Height in pixels
        uint32_t height = (uint32_t) Matrix_dimension;
        fseek(out,22,0);
        fwrite(&height,4,1,out);
    
    
        // Number of planes
        uint16_t planes = 1;
        fseek(out,26,0);
        fwrite(&planes,2,1,out);
    
    
        // Color depth, BPP (bits per pixel)
        uint16_t bpp = 8;
        fseek(out,28,0);
        fwrite(&bpp,2,1,out);
    
    
        // Compression type
        uint32_t compression = 0;
        fseek(out,30,0);
        fwrite(&compression,4,1,out);
    
    
        // Image size in bytes
        uint32_t imagesize = (uint32_t) Matrix_dimension*Matrix_dimension;
        fseek(out,34,0);
        fwrite(&imagesize,4,1,out);
    
    
        // Xppm
        uint32_t xppm = 0;
        fseek(out,38,0);
        fwrite(&xppm,4,1,out);
    
    
        // Yppm
        uint32_t yppm = 0;
        fseek(out,42,0);
        fwrite(&yppm,4,1,out);
    
    
        // Number of color used (NCL)
        uint32_t colours = 256;
        fseek(out,46,0);
        fwrite(&colours,4,1,out);
    
    
        // Number of important color (NIC)
        // value = 0 means all collors important
        uint32_t impcolours = 0;
        fseek(out,50,0);
        fwrite(&impcolours,4,1,out);
    
    
        // Colour table
        unsigned char bmpcolourtable[1024];
        for(ii=0; ii < 1024; ii++){
            bmpcolourtable[ii] =  0;
        }
        jj=3;
        for(ii=0; ii < 255; ii++){
            bmpcolourtable[jj+1] =  ii+1;
            bmpcolourtable[jj+2] =  ii+1;
            bmpcolourtable[jj+3] =  ii+1;
            jj=jj+4;
        }
    
    
        fseek(out,54,0);
        fwrite(&bmpcolourtable,256,4,out);
    
    
        for(ii=0;ii<Matrix_dimension;ii++){
            for(jj=0;jj<Matrix_dimension;jj++){
                pos+= 1;
                fseek(out,pos,0);
                fwrite(&Matrix[ii][jj],(sizeof(unsigned char)),1,out);
            }
        }
    
    
        fflush(out);
        fclose(out);
    }

    I am sure there are some improvements still to be made however I have tried this on several different images and all work.

    Thank you again,
    BN

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Array parsing, would you write it like this?
    By C_ntua in forum C++ Programming
    Replies: 3
    Last Post: 01-10-2013, 11:58 PM
  2. How to read and write an char array
    By Menno in forum C Programming
    Replies: 0
    Last Post: 06-17-2007, 06:17 AM
  3. Write to binary file from 2D array
    By wbaker01 in forum C++ Programming
    Replies: 10
    Last Post: 11-29-2006, 06:16 AM
  4. Using write() to print array elements
    By Unregistered in forum C Programming
    Replies: 1
    Last Post: 04-01-2002, 09:18 PM
  5. How do u write up an array of strings?
    By hot_ice in forum C Programming
    Replies: 1
    Last Post: 11-19-2001, 08:41 PM

Tags for this Thread