I'm having a problem with a code that I made that should display the bytes of the RGB color values of each pixel of an image in bmp (bitmap) format.
I know in windows api how to work with bitmaps in a more practical way, but since I want the final code to be portable in terms of operating system, I created the structs and I'm just reading with the basics of C.
Here's the code:
#include <stdio.h>
#include <stdlib.h>
unsigned char *readBMP(char *filename, int *size) {
int width, height;
unsigned char *data;
unsigned char info[54];
FILE *file = fopen(filename, "rb");
if (file == NULL)
return 0;
fread(info, sizeof(unsigned char), 54, file); // read the 54-byte header
// extract image height and width from header
width = *(int *) &info[18];
height = *(int *) &info[22];
*size = 3 * width * height;
data = (unsigned char *) malloc(*size * sizeof(unsigned char)); // allocate 3 bytes per pixel
fread(data, sizeof(unsigned char), (size_t) *size, file); // read the rest of the data at once
for (int i = 0; i < *size; i += 3) {
unsigned char tmp = data[i];
data[i] = data[i + 2];
data[i + 2] = tmp;
}
fclose(file);
return data;
}
int main() {
int size = 0;
char filename[] = "output.bmp";
unsigned char *data = readBMP(filename, &size);
for (int i = 0; i < size; i++) {
printf("%d. %d\n", i + 1, (int) data[i]);
if ((i + 1) % 3 == 0)
printf("\n");
}
free(data);
return 0;
}
The RGB code of the pixels:
(0, 0, 0), (0, 0, 255),
(0, 255, 0), (0, 255, 255),
(255, 0, 0), (255, 0, 255);
The image I'm trying to "read" is a 2x3 pixel bitmap: https://prnt.sc/gnygch
And the output I have is:
1. 255
2. 0
3. 0
4. 255
5. 0
6. 255
7. 0
8. 0
9. 0
10. 255
11. 0
12. 255
13. 0
14. 0
15. 255
16. 0
17. 0
18. 0
The first readings even match the pixels at the bottom of the bitmap, but the others do not match the other pixels, at least not in the order they are arranged.
Can anyone see what I'm doing wrong?
The size of the bitmap is not width * height * 3. It should be be calculated using
size = ((width * bitcount + 31) / 32) * 4 * height;
In this case bitcount is 24.
The rows in 24-bit bitmaps have to be padded. You also need to make sure you are reading a 24-bit bitmaps. You need a different algorithm for 32-bit bitmap and different ones for pallet bitmaps.
The rows are read from bottom to top.
Below is an example for 24-bit.
You may still run in to other problems with this code. It's better to use a library for these functions. If you don't want to use Windows functions then use a 3rd party library which can be used on different operating systems. There are many such libraries out there.
int main()
{
int width, height, padding, bitcount, size;
unsigned char *data = 0;
unsigned char info[54] = { 0 };
FILE *file = fopen("output.bmp", "rb");
if(!file)
return 0;
fread(info, 1, 54, file);
width = *(int*)(info + 18);
height = *(int*)(info + 22);
bitcount = *(int*)(info + 28);
size = ((width * bitcount + 31) / 32) * 4 * height;
padding = width % 4;
if(bitcount != 24) //this code works for 24-bit bitmap only
goto error;
data = malloc(size);
fread(data, 1, size, file);
for(int row = height - 1; row >= 0; row--)
{
for(int col = 0; col < width; col++)
{
int p = (row * width + col) * 3 + row * padding;
printf("%02X%02X%02X ", data[p + 0], data[p + 1], data[p + 2]);
}
printf("\n");
}
error:
if(file) fclose(file);
if(data) free(data);
return 0;
}
Related
I'm trying to convert 480x280 24-bit bitmap file to a C array (1-bit per pixel). Here is my code which is based on this answer:
int main(void)
{
int i, j;
int avg = 0;
unsigned int line = 0;
unsigned char byte = 0;
unsigned char *ptr;
FILE* f = fopen("test.bmp", "rb");
unsigned char info[54];
// read the 54-byte header
fread(info, sizeof(unsigned char), 54, f);
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
// allocate 3 bytes per pixel
int size = 3 * 280 * 480;
unsigned char* data = (unsigned char*)malloc(size);
// read the rest of the data at once
fread(data, sizeof(unsigned char), size, f);
fclose(f);
/* convert 24-bit to 1-bit -> each 3 bytes (r,g,b) to 1 bit (black,white) */
for(i = 0; i < size; i += 24)
{
ptr = &data[i];
/* take 24 bytes in each iteration (3 colors per pixel x 8 pixels) */
for(j = 0; j < 24; j += 3)
{
/* convert 3 bytes to 1 byte */
avg = (ptr[j] + ptr[j+1] + ptr[j+2]) / 3;
/* convert 1 byte to white or black bit */
if(avg > 128)
byte |= (1 << (j / 3)); /* black */
else
byte |= (0 << (j / 3)); /* white */
}
printf("0x%02X,", byte);
byte = 0;
avg = 0;
if(++line == 16)
{
printf("\n");
line = 0;
}
}
}
This does the job but the result array produces edgy image. Check these original and converted images:
What am I missing to get a smooth result image?
the image You Convert Is RGB888 may be your Runnning Controller accepct ARGB888.
Convert RGB Image With Alpha ARGB888.
I am trying to convert 24bpp BMP color image to 8bpp grayscale BMP image. I have done something but i am not getting 100% correct output.
I have converted 24bpp to 8bpp. but in result image colour table is also being considered as pixel data. I have tried setting offset byte in header but problem still persists.
#include <stdio.h>
int main()
{
FILE* fIn = fopen("lena_colored_256.bmp","rb");
FILE* fOut = fopen("lena_gray.bmp", "w+");
int i, j, y;
unsigned char byte[54];
unsigned char colorTable[1024];
if (fIn == NULL)// check if the input file has not been opened
{
printf("File does not exist.\n");
}
for (i = 0; i < 54; i++)//read the 54 byte header from fIn
{
byte[i] = getc(fIn);
}
byte[28] = (unsigned char)8; // write code to modify bitDepth to 8
byte[11] = (unsigned char)04;
//write code to add colorTable
for (i = 0; i < 256; i++)
{
colorTable[i * 4 + 0] = i;
colorTable[i * 4 + 1] = i;
colorTable[i * 4 + 2] = i;
colorTable[i * 4 + 3] = i;
}
for (i = 0; i < 54; i++)
{
putc(byte[i], fOut);
}
for (i = 0; i < 1024; i++)
{
putc(colorTable[i], fOut);
}
// extract image height, width and bitDepth from imageHeader
int *height = (int)& byte[18];
int *width = (int)& byte[22];
int *bitDepth = (int)& byte[28];
printf("bitDepth : %d\n", *bitDepth);
printf("width: %d\n", *width);
printf("height: %d\n", *height);
int size = (*height) * (*width)*3; //calculate image size
unsigned char* buffer;
buffer = (unsigned char*)malloc(sizeof(int) * size);
for (i = 0; i < size; i=i+3) //RGB to gray
{
buffer[i+2] = getc(fIn);//blue
buffer[i+1] = getc(fIn);//green
buffer[i+0] = getc(fIn);//red
y = (buffer[i+0] * 0.33) + (buffer[i+1] * 0.33) + (buffer[i+2] * 0.33);
putc(y, fOut);
}
fclose(fOut);
fclose(fIn);
return 0;
}
Colour table data is also being considered as pixel data by image.
i have checked my colour table data entering into BMP file. I have printed out file pointer location, after entering at 94th byte it is increasing by 2 byte instead of 1 byte, this is happening total 4 times and other 1020 time file pointer is increasing by 1 byte. Any explanation regarding this?
Changing 24-bit to 8-bit bitmap is not as simple as changing the bitcount in header file. 24-bit bitmap doesn't have a color table. You have to build a color table for the 8-bit. Fortunately this is relatively easy for gray scale images.
Several values in bitmap header file need to be modified.
Then change the 24-bit to gray scale, and change to 8-bit bitmap. See bitmap file format for additional information. Also read about "padding" where the width of bitmap in bytes should always be a multiple of 4.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#pragma pack(push, 2)
typedef struct {
int16_t bfType;
int32_t bfSize;
int16_t bfReserved1;
int16_t bfReserved2;
int32_t bfOffBits;
} BITMAPFILEHEADER;
typedef struct {
int32_t biSize;
int32_t biWidth;
int32_t biHeight;
int16_t biPlanes;
int16_t biBitCount;
int32_t biCompression;
int32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
int32_t biClrUsed;
int32_t biClrImportant;
} BITMAPINFOHEADER;
#pragma pack(pop)
int main()
{
FILE* fin = fopen("fin.bmp", "rb");
FILE* fout = fopen("fout.bmp", "wb");
if(!fin) { printf("fin error\n"); goto error; }
if(!fout) { printf("fout error\n"); goto error; }
BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;
fread(&bf, sizeof bf, 1, fin);
fread(&bi, sizeof bi, 1, fin);
if(sizeof bf != 14) { printf("Wrong pragma pack\n"); goto error; }
if(sizeof bi != 40) { printf("Wrong pragma pack\n"); goto error; }
if(bf.bfType != 0x4D42) { printf("not bmp, or not LE system\n"); goto error; }
if(bi.biSize != 40) { printf("Can't handle this bitmap format\n"); goto error; }
if(bi.biBitCount != 24) { printf("not 24-bit\n"); goto error; }
int height = bi.biHeight;
if(height < 0) height = -height;
//width in bytes:
int src_wb = ((bi.biWidth * 24 + 31) / 32) * 4;
int dst_wb = ((bi.biWidth * 8 + 31) / 32) * 4;
int src_size = src_wb * height;
int dst_size = dst_wb * height;
//allocate for source and destination
uint8_t *src = malloc(src_size);
uint8_t *dst = malloc(dst_size);
//read pixels
fread(src, 1, src_size, fin);
//make gray scale color-table
uint8_t clr[1024] = { 0 };
for(int i = 0; i < 256; i++)
clr[i * 4 + 0] = clr[i * 4 + 1] = clr[i * 4 + 2] = (uint8_t)i;
for(int y = height - 1; y >= 0; y--)
{
for(int x = 0; x < bi.biWidth; x++)
{
uint8_t blu = src[y * src_wb + x * 3 + 0];
uint8_t grn = src[y * src_wb + x * 3 + 1];
uint8_t red = src[y * src_wb + x * 3 + 2];
uint8_t gry = (uint8_t)(.33 * red + .34 * grn + .33 * blu);
dst[y * dst_wb + x] = gry; //this will match the index in color-table
}
}
//modify bitmap headers
bf.bfSize = 54 + 1024 + dst_size;
bi.biBitCount = 8;
bi.biSizeImage = dst_size;
fwrite(&bf, sizeof bf, 1, fout);
fwrite(&bi, sizeof bi, 1, fout);
fwrite(clr, 1, 1024, fout);
fwrite(dst, 1, dst_size, fout);
free(src);
free(dst);
error:
fclose(fout);
fclose(fin);
return 0;
}
FILE* fOut = fopen("lena_gray.bmp", "w+");//ERROR
FILE* fOut = fopen("lena_gray.bmp", "wb");//TRUE
I've been assigned to create a barcode image from a number generated randomly by me (that I've already done). So far I tried to create a BMP file and put in a simple black and white columns but my picture is distorted by other colors and not even in columns. I didn't even start to write the barcode itself (that itself is still a mystery to me), I tried to create it for almost 2 weeks now and to no avail.. I mostly need a program that writes black or white to the program by columns or row by row so i can put black or white at will.
This is my code:
`int width, hight;
width = 141;
hight = 70;
FILE* barcode;
fopen_s(&barcode,NEWBARCODE, "wb");
int filesize = 54 + 3 * width*height; //w is your image width, h is image height, both int
char bmpfileheader[14] = { 'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0 };
char bmpinfoheader[40] = { 0x28,0,0,0, 141,0,0,0, 70,0,0,0, 1,0, 24,0,0,0,0,0,0x8c,0x05,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
unsigned char white[3] = {255,255,255 };
unsigned char black[3] = {0,0,0 };
unsigned char pad[1] = { 0 };
bmpfileheader[2] = (unsigned char)(filesize);
bmpfileheader[3] = (unsigned char)(filesize >> 8);
bmpfileheader[4] = (unsigned char)(filesize >> 16);
bmpfileheader[5] = (unsigned char)(filesize >> 24);
fwrite(&bmpfileheader, 1, 14, barcode);
fwrite(&bmpinfoheader, 1, 40, barcode);
for (int i = 0; i < height; i++) { //columns
for (int j = 0; j < width*3+1; j++) {
if (i % 2 == 0) {
fwrite(&white, 1, 3, barcode);
fwrite(&black, 1, 3, barcode);
}
else {
fwrite(&black, 1, 3, barcode);
fwrite(&white, 1, 3, barcode);
}
}
fwrite(&pad, 1, 1, barcode);
}
and that outputs the bmp file
What is wrong? And if there are any tips to work on creating a bmp file would be greatly appreciated :)
Try the below. Note that I've moved the BMP to a bit depth of 32. The wrapper function setColumn will allow you to set individual columns to black or white as you see fit. Much more manageable to think of the BMP as an array that you can freely manipulate instead of having to deal with a ton of fwrite logic.
#include "stdafx.h"
#include <fstream>
#define NEWBARCODE "test.bmp"
#define WHITE 255
#define BLACK 0
void setColumn(unsigned char *data, const int height, const int width, const int colIndex, const int grayVal)
{
for (int r = 0; r < height; ++r)
{
data[r * width * 4 + colIndex * 4 + 0] = grayVal;
data[r * width * 4 + colIndex * 4 + 1] = grayVal;
data[r * width * 4 + colIndex * 4 + 2] = grayVal;
data[r * width * 4 + colIndex * 4 + 3] = 255;
}
}
int main()
{
int width, height;
width = 141;
height = 70;
std::ofstream filestream;
filestream.open(NEWBARCODE, std::ios::beg | std::ios::out | std::ios::binary);
int filesize = 54 + 4 * width * height;
char bmpfileheader[14] = { 'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0 };
char bmpinfoheader[40] = { 0x28,0,0,0, 141,0,0,0, 70,0,0,0, 1,0, 32,0,0,0,0,0,0x8c,0x05,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
bmpfileheader[2] = (unsigned char)(filesize);
bmpfileheader[3] = (unsigned char)(filesize >> 8);
bmpfileheader[4] = (unsigned char)(filesize >> 16);
bmpfileheader[5] = (unsigned char)(filesize >> 24);
filestream.write(bmpfileheader, 14);
filestream.write(bmpinfoheader, 40);
//Allocate BMP data block
unsigned char *data = new unsigned char[width * height * 4]{ 0 };
//Initialize BMP data to all black pixels
for (int i = 0; i < width * height * 4; ++i)
data[i] = 0;
//Set white
for (int i = 75; i < 100; ++i)
setColumn(data, height, width, i, WHITE);
//Set white
for (int i = 15; i < 25; ++i)
setColumn(data, height, width, i, WHITE);
//Set black
for (int i = 20; i < 23; ++i)
setColumn(data, height, width, i, BLACK);
filestream.write((const char *)data, height * width * 4);
filestream.close();
delete data;
return 0;
}
I have a pixel information array who has the biSizeImage size and contains triplets of the form (blue, green, red) with values between 0 and 255. I must change my bmp color from red, to white, but i have a problem, as can be seen in picture.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
BITMAPFILEHEADER file;
BITMAPINFOHEADER info;
FILE* f=fopen("imagine.bmp","rb");
fread(&file,sizeof(BITMAPFILEHEADER),1,f);
fread(&info,sizeof(BITMAPINFOHEADER),1,f);
RGBQUAD *a=malloc(info.biSizeImage*sizeof(RGBQUAD));
fread(a,sizeof(RGBQUAD),info.biSizeImage,f);
for(int i=0;i<info.biSizeImage;i++)
{
a[i].rgbBlue=255;
a[i].rgbGreen=255;
a[i].rgbRed=255;
}
FILE* f2=fopen("imagine2.bmp","wb");
if(f2==NULL)
printf("Error");
fwrite(&file,sizeof(BITMAPFILEHEADER),1,f2);
fwrite(&info,sizeof(BITMAPINFOHEADER),1,f2);
fwrite(a,sizeof(RGBQUAD),info.biSizeImage,f2);
return 0;
Output
Original image
info.biSizeImage can be zero. Check to make sure it is not zero, or calculate it based on stride. Width, height, and bitcount values are always set.
As noted in comments, 24 bit image has 3 bytes (24 bit) per pixel. RGBQUA is for 32-bit image. You have to iterate through the height, and then width 3 bytes at a time.
int main(void)
{
BITMAPFILEHEADER file;
BITMAPINFOHEADER info;
FILE* f = fopen("imagine.bmp", "rb");
fread(&file, sizeof(BITMAPFILEHEADER), 1, f);
fread(&info, sizeof(BITMAPINFOHEADER), 1, f);
if(info.biBitCount != 24)
return 0;
int stride = ((info.biWidth * info.biBitCount + 31) / 32) * 4;
int size = stride * info.biHeight;
BYTE *a = malloc(stride * info.biHeight);
fread(a, 1, size, f);
for(int y = info.biHeight - 1; y >= 0; y--)
{
for(int x = 0; x < stride; x += 3)
{
int i = y * stride + x;
a[i + 0] = 255;
a[i + 1] = 255;
a[i + 2] = 255;
}
}
FILE* f2 = fopen("imagine2", "wb");
if(f2 == NULL)
printf("Error");
fwrite(&file, sizeof(BITMAPFILEHEADER), 1, f2);
fwrite(&info, sizeof(BITMAPINFOHEADER), 1, f2);
fwrite(a, 1, size, f2);
return 0;
}
#include < stdio.h >
#include < conio.h >
#include < stdlib.h >
#include < process.h >
#include < string.h >
#include < math.h >
int count = 0;
typedef struct bitmap24 {
unsigned char header[54];
unsigned char * pixels;
}BMP;
void readBMP(char * filename) {
int i;
FILE * f = fopen(filename, "rb");
FILE * f1 = fopen("save.bmp", "wb");
FILE * pixelVals = fopen("vals.dat", "w");
unsigned char bmppad[3] = {
0,
0,
0
};
if (!f) {
printf("Could not read file!\n");
exit(0);
}
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f);
int width = * (int * ) & info[18];
int height = * (int * ) & info[22];
unsigned char * img = NULL;
if (img)
free(img);
img = (unsigned char * ) malloc(3 * width * height);
memset(img, 0, sizeof(img));
fwrite(info, sizeof(unsigned char), 54, f1);
int length = width * height;
unsigned long int image[10000][3];
for (i = 0; i < length; i++) {
image[i][2] = getc(f); // blue
image[i][1] = getc(f); // green
image[i][0] = getc(f); // red
img[count] = 255 - (unsigned char) image[i][0];
//img[count] = 10*(unsigned char)log10((double)image[i][0]+1);
count += 1;
img[count] = 255 - (unsigned char) image[i][2];
//img[count] = 10*(unsigned char)log10((double)image[i][3]+1);
count += 1;
img[count] = 255 - (unsigned char) image[i][2];
//img[count] = 10*(unsigned char)log10((double)image[i][2]+1);
count += 1;
printf("pixel %d : [%d,%d,%d]\n", i + 1, image[i][0], image[i][4], image[i][2]);
fprintf(pixelVals, "pixel %d : [%d,%d,%d]\n", i + 1, image[i][0], image[i][5], image[i][2]);
}
for (i = height - 1; i >= 0; i--) {
fwrite(img + (width * (height - i - 1) * 3), 3, width, f1);
fwrite(bmppad, 1, (4 - (width * 3) % 4) % 4, f1);
}
fclose(f);
fclose(f1);
fclose(pixelVals);
}
void main() {
char * fileName = "bitgray.bmp";
readBMP(fileName);
getch();
}
I am not getting the correct result when the image is saved. I am using 24bit bmp image of dimensions 114 X 81. The image was coming out to be inverted initially but that issue was solved. But I am still getting a slanted image. I know the problem is in the last 'for' loop.
How should I solve it ?
Bitmap scanlines are padded to 4-byte boundary. So you need to add an extra two bytes so that the row is divisible by 4. At the moment, you have 114 * 3 = 342 bytes of pixel data per line. The next number divisible by 4 is 344.
So, at the end of reading each line, just read an extra two bytes and discard them.
In general, you can work out the extra bytes like this:
extra = (alignment - ((width * bytesPerPixel) % alignment)) % alignment;
Where in this case alignment is 4.
From memory, there is a field in the header that should contain the value of the full scanwidth (width * bytesPerPixel + extra), but it's a good idea not to expect it to be correct because you can calculate it easily.
You must also be aware of this padding rule when you save a bitmap.
Your second for loop looks strange. I believe it should be:
for(i = 0; i < height; i++) {...}
or:
for(i = height-1; i >= 0; i--) {...}