Converting 24 bit bmp to 8 bit grayscale

Hello! I am trying to write a bmp class. I became interested while studying pattern matching in artificial intelligence. For class, we had to write an algorithm that would detect edges in an image. The algorithm did not need to actually be implemented but I decided to implement it anyways for learning purposes. I realize that there are various libraries already written which will do what I'm trying to do but again this is for learning purposes. The problem I'm having is in converting a 24 bit color bmp to an 8 bit grayscale image. In my code I've made a note of the function I'm struggling with. The problem I'm having is that the file created by SendGrayScale is 264246 bytes when it should be 263222 bytes and of course it won't open...

gs.dat is my RGB palette for the 8 bit image. I created it by reading a grayscale image, actually a grayscale version of the color image I'm working with. I read it using this class. At this point, the class correctly copies both color and grayscale images so I believe that gs.dat has been created properly.

Any help would be greatly appreciated.

The following post is my entire class. Thanks...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
#include "bmpclass.h"

BitMapClass::BitMapClass(char *fileName)
{
	clrPalette = NULL;
	fin.open(fileName, ios::in | ios::binary);
	
	GetFileHeader();
	GetImgInfo();
	GetRGBPalette();
	GetPixelData();
}
	
void BitMapClass::GetFileHeader()
{
	if (!fin.is_open()) throw string("File Read Error");
	else
	{
		fin.read((char*)&fileHeader.bfType, sizeof(fileHeader.bfType));
		if (fileHeader.bfType != 19778) throw string("Invalid Filetype");
		else
		{
			fin.read((char*)&fileHeader.bfSize, sizeof(fileHeader.bfSize));
			fin.read((char*)&fileHeader.bfRes1, sizeof(fileHeader.bfRes1));
			fin.read((char*)&fileHeader.bfRes2, sizeof(fileHeader.bfRes2));
			fin.read((char*)&fileHeader.bfOffBits, sizeof(fileHeader.bfOffBits));
		}
	}
}

void BitMapClass::GetImgInfo()
{
	if (!fin.is_open()) throw string("File Read Error");
	else
	{
		fin.read((char*)&imgInfo.biSize, sizeof(imgInfo.biSize));
		fin.read((char*)&imgInfo.biWidth, sizeof(imgInfo.biWidth));
		fin.read((char*)&imgInfo.biHeight, sizeof(imgInfo.biHeight));
		fin.read((char*)&imgInfo.biPlanes, sizeof(imgInfo.biPlanes));
		fin.read((char*)&imgInfo.biBitCount, sizeof(imgInfo.biBitCount));
		fin.read((char*)&imgInfo.biCompression, sizeof(imgInfo.biCompression));
		fin.read((char*)&imgInfo.biSizeImage, sizeof(imgInfo.biSizeImage));
		fin.read((char*)&imgInfo.biXPelsPerMeter, sizeof(imgInfo.biXPelsPerMeter));
		fin.read((char*)&imgInfo.biYPelsPerMeter, sizeof(imgInfo.biYPelsPerMeter));
		fin.read((char*)&imgInfo.biClrUsed, sizeof(imgInfo.biClrUsed));
		fin.read((char*)&imgInfo.biClrImportant, sizeof(imgInfo.biClrImportant));
	}
}

void BitMapClass::GetPixelData()
{
	int size = fileHeader.bfSize - fileHeader.bfOffBits;
	char *tempPixelData = new char[size];
	
	for (int i=0; i < size; i++)
		fin.read((char*)&tempPixelData[i], 1);
	
	unsigned long byteWidth, padWidth;
	
	byteWidth = padWidth = (unsigned long)((float)imgInfo.biWidth*(float)imgInfo.biBitCount/8.0);
	while (padWidth % 4 != 0)
		padWidth++;
	
	dword diff;
	int offset;
	long height;
	
	height = imgInfo.biHeight;
	diff = height * byteWidth;
	
	pixelData = new char[diff];
	
	if (height > 0)
	{
		int j = imgInfo.biSize - 3;
		offset = padWidth - byteWidth;
		for (int i=0; i < imgInfo.biSize; i += 3)
		{
			if ((i + 1) % padWidth == 0)
				i += offset;
			*(pixelData + j + 2) = *(tempPixelData + i);
			*(pixelData + j + 1) = *(tempPixelData + i + 1);
			*(pixelData + j) = *(tempPixelData + i + 2);
			j++;
		}
	}
	
	for (int i=0; i < size; i++)
		pixelData[i] = tempPixelData[i];

	fin.close();
}

void BitMapClass::GetRGBPalette()
{
	if (!fin.is_open()) throw string("File Read Error");
	else
	{
		int numColors=1 << imgInfo.biBitCount;
		clrPalette = new RGBPalette[numColors];

 		if (imgInfo.biBitCount < 24)
		{
			for (int i=0; i < numColors; i++)
			{
				fin.read(&clrPalette[i].rgbBlue, 1);
				fin.read(&clrPalette[i].rgbGreen, 1);
				fin.read(&clrPalette[i].rgbRed, 1);
				fin.read(&clrPalette[i].rgbReserved, 1);
			}
		}
	}
}

//
void BitMapClass::SendGrayScale(ofstream& fout)
{	
	/*	This is the method I'm having trouble with	*/
	
	word *biBitCount = new word(8);
	int numColors=1 << *biBitCount;
	dword *bfSize = new dword(54 + (4 * numColors) + (imgInfo.biWidth * imgInfo.biHeight));
	dword *size = new dword(*bfSize - fileHeader.bfOffBits);
	char *gPixelData = new char[(*size)];
	int j = 0;
	
	for (int i=0; i < (fileHeader.bfSize - fileHeader.bfOffBits); i+=3)
	{
		gPixelData[j] = (float)(((pixelData[i])*0.299) 
			                + ((pixelData[i+1])*0.587) 
			                + ((pixelData[i+2])*0.114));
		j++;
	}
	
	if (fout.is_open())
	{
		fout.write((char*)&fileHeader.bfType, sizeof(fileHeader.bfType));
		fout.write((char*)&bfSize, sizeof(dword));
		fout.write((char*)&fileHeader.bfRes1, sizeof(fileHeader.bfRes1));
		fout.write((char*)&fileHeader.bfRes2, sizeof(fileHeader.bfRes2));
		fout.write((char*)&fileHeader.bfOffBits, sizeof(fileHeader.bfOffBits));
		fout.write((char*)&size, sizeof(dword));
		fout.write((char*)&imgInfo.biWidth, sizeof(imgInfo.biWidth));
		fout.write((char*)&imgInfo.biHeight, sizeof(imgInfo.biHeight));
		fout.write((char*)&imgInfo.biPlanes, sizeof(imgInfo.biPlanes));
		fout.write((char*)&biBitCount, sizeof(imgInfo.biBitCount));
		fout.write((char*)&imgInfo.biCompression, sizeof(imgInfo.biCompression));
		fout.write((char*)&imgInfo.biSizeImage, sizeof(imgInfo.biSizeImage));
		fout.write((char*)&imgInfo.biXPelsPerMeter, sizeof(imgInfo.biXPelsPerMeter));
		fout.write((char*)&imgInfo.biYPelsPerMeter, sizeof(imgInfo.biYPelsPerMeter));
		fout.write((char*)&imgInfo.biClrUsed, sizeof(imgInfo.biClrUsed));
		fout.write((char*)&imgInfo.biClrImportant, sizeof(imgInfo.biClrImportant));

		ifstream clrIn;
		clrIn.open("gs.dat", ios::in | ios::binary);
		
		if (clrPalette)
			delete clrPalette;
		
		clrPalette = new RGBPalette[numColors];
		
		for (int i=0; i < numColors; i++)
		{
			clrIn.read(&clrPalette[i].rgbBlue, 1);
			clrIn.read(&clrPalette[i].rgbGreen, 1);
			clrIn.read(&clrPalette[i].rgbRed, 1);
			clrIn.read(&clrPalette[i].rgbReserved, 1);
			fout.write(&clrPalette[i].rgbBlue, 1);
			fout.write(&clrPalette[i].rgbGreen, 1);
			fout.write(&clrPalette[i].rgbRed, 1);
			fout.write(&clrPalette[i].rgbReserved, 1);
		}
		
		for (int i=0; i < (*size); i++)
		{
			fout.write(&gPixelData[i], 1);
		}
		
	}
}
//

void BitMapClass::SendToFile(ofstream& fout)
{
	dword size = fileHeader.bfSize - fileHeader.bfOffBits;
	int numColors=1 << imgInfo.biBitCount;
	
	if (fout.is_open())
	{
		fout.write((char*)&fileHeader.bfType, sizeof(fileHeader.bfType));
		fout.write((char*)&fileHeader.bfSize, sizeof(fileHeader.bfSize));
		fout.write((char*)&fileHeader.bfRes1, sizeof(fileHeader.bfRes1));
		fout.write((char*)&fileHeader.bfRes2, sizeof(fileHeader.bfRes2));
		fout.write((char*)&fileHeader.bfOffBits, sizeof(fileHeader.bfOffBits));
		fout.write((char*)&imgInfo.biSize, sizeof(imgInfo.biSize));
		fout.write((char*)&imgInfo.biWidth, sizeof(imgInfo.biWidth));
		fout.write((char*)&imgInfo.biHeight, sizeof(imgInfo.biHeight));
		fout.write((char*)&imgInfo.biPlanes, sizeof(imgInfo.biPlanes));
		fout.write((char*)&imgInfo.biBitCount, sizeof(imgInfo.biBitCount));
		fout.write((char*)&imgInfo.biCompression, sizeof(imgInfo.biCompression));
		fout.write((char*)&imgInfo.biSizeImage, sizeof(imgInfo.biSizeImage));
		fout.write((char*)&imgInfo.biXPelsPerMeter, sizeof(imgInfo.biXPelsPerMeter));
		fout.write((char*)&imgInfo.biYPelsPerMeter, sizeof(imgInfo.biYPelsPerMeter));
		fout.write((char*)&imgInfo.biClrUsed, sizeof(imgInfo.biClrUsed));
		fout.write((char*)&imgInfo.biClrImportant, sizeof(imgInfo.biClrImportant));
		if (imgInfo.biBitCount < 24)
		{
			for (int i=0; i < numColors; i++)
			{
				fout.write(&clrPalette[i].rgbBlue, 1);
				fout.write(&clrPalette[i].rgbGreen, 1);
				fout.write(&clrPalette[i].rgbRed, 1);
				fout.write(&clrPalette[i].rgbReserved, 1);
			}
		}
		for (int i=0; i < size; i++)
		{
			fout.write(&pixelData[i], 1);
		}
		
	}
}

void BitMapClass::TextDumpFileHeader(ostream& out)
{
	if (!out) throw string("Invalid Output Stream");
	else
	{
		cout << fileHeader.bfType << endl;
		cout << fileHeader.bfSize << endl;
		cout << fileHeader.bfRes1 << endl;
		cout << fileHeader.bfRes2 << endl;
		cout << fileHeader.bfOffBits << endl;
	}
}

void BitMapClass::TextDumpImgInfo(ostream& out)
{
	if (!out) throw string("Invalid Output Stream");
	else
	{
		cout << imgInfo.biSize << endl;
		cout << imgInfo.biWidth << endl;
		cout << imgInfo.biHeight << endl;
		cout << imgInfo.biPlanes << endl;
		cout << imgInfo.biBitCount << endl;
		cout << imgInfo.biCompression << endl;
		cout << imgInfo.biSizeImage << endl;
		cout << imgInfo.biXPelsPerMeter << endl;
		cout << imgInfo.biYPelsPerMeter << endl;
		cout << imgInfo.biClrUsed << endl;
		cout << imgInfo.biClrImportant << endl;
	}
}

void BitMapClass::TextDumpClrPalette(ostream& out)
{
	int numColors=1 << imgInfo.biBitCount;
	
	for (int i=0; i < numColors; i++)
	{
		cout << (int)clrPalette[i].rgbBlue << '\t';
		cout << (int)clrPalette[i].rgbGreen << '\t';
		cout << (int)clrPalette[i].rgbRed << '\t';
		cout << (int)clrPalette[i].rgbReserved << endl;
	}
}

BitMapClass::~BitMapClass()
{
}
The formula for grayscale is:

S=(R*0.3+G*0.59+B*0.11)

or with only integers:

S=(R*30+G*59+B*11)/10

Where:
S=shade of gray (intensity)
R=red
G=green
B=blue


Source:
http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
did you look at my code?
Check for memory leaks (everything that is allocated with new should be deallocated with delete)
122
123
	dword *bfSize = new dword(54 + (4 * numColors) + (imgInfo.biWidth * imgInfo.biHeight));
	dword *size = new dword(*bfSize - fileHeader.bfOffBits);
Maybe those could be avoided

127
128
129
130
131
char *gPixelData = new char[(*size)];
for (int K=0, L=0; L < size; K+=3, L++) //maybe out of bounds
	gPixelData[L] = (float)(((pixelData[K])*0.299) //gPixel is char, why are you casting to float?
			                + ((pixelData[K+1])*0.587) 
			                + ((pixelData[K+2])*0.114));


138
139
//dword *bfSize;
fout.write((char*)&bfSize, sizeof(dword));
bfSize is a pointer, saving its content is futile (also you dimension is incorrect).
Maybe you want fout.write( (char *)bfSize, sizeof(*bfSize) );
Last edited on
lol.. As I was copying my code into my post I thought "I wonder if anyone will say anything about not cleaning up my memory..." For the moment I'm not too worried about memory leaks. :)

I cast gPixel to float so that I can get the numerical value for the colors...
Perhaps I don't need to...

I see what you're saying about bfSize being a pointer...
bfSize is a dword pointer so I don't see how the dimension is wrong.

I will play around with your suggestions. I'm thinking it may be the for loop as you said it may be out of bounds.

Thank you
bfSize is a dword pointer so I don't see how the dimension is wrong.
If you save a pointer the size must be the one of the pointer fout.write((char*)&bfSize, sizeof(bfSize) );. However saving a pointers makes no sense.

1
2
3
4
5
/*for (int i=0; i < size; i++){
	fout.write(&pixelData[i], 1);
}*/
fout.write( pixelData, sizeof(pixelData) ); //if is not dynamical allocated
fout.write( pixelData, sizeof(pixelData[0])*size ); //maybe there is a way without dereferencing 


1
2
3
4
5
6
7
for (int i=0; i < numColors; i++)
			{
				fout.write(&clrPalette[i].rgbBlue, 1);
				fout.write(&clrPalette[i].rgbGreen, 1);
				fout.write(&clrPalette[i].rgbRed, 1);
				fout.write(&clrPalette[i].rgbReserved, 1);
			}
If you change your structure, you could use the same trick
My understanding about write is that it takes a pointer to a block of memory then the size of the block so it's not saving the pointer but the data starting at the pointer and ending at the end of the block size... Maybe I'm missing something here?

I had initially tried saving the whole block at one time as you have shown but I didn't take into account that the array was dynamically allocated. I see now why it didn't work. I will give that a try again...

Thanks.
My understanding about write is that it takes a pointer to a block of memory
Yes, but you were doing &bfSize (a pointer to the pointer)
ah, yep... I see it now... Thanks. Things are starting to clear up now.
Topic archived. No new replies allowed.