Commit d9b2b2a850d0a6d6044fe2d01eff09e0754ff0c1
1 parent
276f9b23
enabled basic reading/writing of BMP files without an external library
Showing
3 changed files
with
329 additions
and
4 deletions
Show diff stats
1 | +#ifndef STIM_GRID3_H | |
2 | +#define STIM_GRID3_H | |
3 | + | |
4 | +namespace stim{ | |
5 | + | |
6 | +template<typename T, typename F = float> | |
7 | +class grid : public stim::grid<T, 3, F>{ | |
8 | + | |
9 | +public: | |
10 | + | |
11 | + /// Convert grid coordinates (integers) into world coordinates (F) based on the pixel spacing | |
12 | + void grid2volume(size_t xi, size_t yi, size_t zi, F& x, F& y, F&z){ | |
13 | + | |
14 | + } | |
15 | + | |
16 | + /// Use linear interpolation to get a value from the grid at (x, y, z) in VOLUME space (based on voxel size) | |
17 | + T lerp(F x, F y, F z){ | |
18 | + | |
19 | + } | |
20 | + /// Create a resampled grid with isotropic voxel sizes | |
21 | + grid3<T, F> resample_iso(){ | |
22 | + | |
23 | + //find the smallest spacing | |
24 | + //create a new grid of the appropriate size | |
25 | + //use linear interpolation to resample the old grid into the new grid | |
26 | + } | |
27 | + | |
28 | + | |
29 | +}; | |
30 | +} //end namespace stim | |
31 | + | |
32 | +#endif | |
0 | 33 | \ No newline at end of file | ... | ... |
1 | +#pragma once | |
2 | + | |
3 | +namespace stim { | |
4 | +#pragma pack(1) | |
5 | + typedef unsigned int DWORD; | |
6 | + typedef unsigned short WORD; | |
7 | + typedef signed int LONG; | |
8 | + typedef struct tagBITMAPFILEHEADER { | |
9 | + WORD bfType; | |
10 | + DWORD bfSize; | |
11 | + WORD bfReserved1; | |
12 | + WORD bfReserved2; | |
13 | + DWORD bfOffBits; | |
14 | + } BITMAPFILEHEADER, *PBITMAPFILEHEADER; | |
15 | + | |
16 | + const unsigned int DIB_BITMAPCOREHEADER = 12; | |
17 | + const unsigned int DIB_OS21XBITMAPHEADER = 16; | |
18 | + const unsigned int DIB_BITMAPINFOHEADER = 40; | |
19 | + const unsigned int DIB_BITMAPV2INFOHEADER = 52; | |
20 | + const unsigned int DIB_BITMAPV3INFOHEADER = 56; | |
21 | + const unsigned int DIB_OS22XBITMAPHEADER = 64; | |
22 | + const unsigned int DIB_BITMAPV4HEADER = 108; | |
23 | + const unsigned int DIB_BITMAPV5HEADER = 124; | |
24 | + | |
25 | + typedef struct tagBITMAPCOREHEADER { | |
26 | + DWORD bcSize; | |
27 | + WORD bcWidth; | |
28 | + WORD bcHeight; | |
29 | + WORD bcPlanes; | |
30 | + WORD bcBitCount; | |
31 | + } BITMAPCOREHEADER, *PBITMAPCOREHEADER; | |
32 | + | |
33 | + typedef struct tagBITMAPINFOHEADER { | |
34 | + DWORD biSize; //40 bytes | |
35 | + LONG biWidth; | |
36 | + LONG biHeight; | |
37 | + WORD biPlanes; | |
38 | + WORD biBitCount; | |
39 | + DWORD biCompression; | |
40 | + DWORD biSizeImage; | |
41 | + LONG biXPelsPerMeter; | |
42 | + LONG biYPelsPerMeter; | |
43 | + DWORD biClrUsed; | |
44 | + DWORD biClrImportant; | |
45 | + } BITMAPINFOHEADER, *PBITMAPINFOHEADER; | |
46 | + | |
47 | + // From FileFormat.info | |
48 | + typedef struct { | |
49 | + DWORD Size; /* Size of this header in bytes */ | |
50 | + LONG Width; /* Image width in pixels */ | |
51 | + LONG Height; /* Image height in pixels */ | |
52 | + WORD Planes; /* Number of color planes */ | |
53 | + WORD BitsPerPixel; /* Number of bits per pixel */ | |
54 | + DWORD Compression; /* Compression methods used */ | |
55 | + DWORD SizeOfBitmap; /* Size of bitmap in bytes */ | |
56 | + LONG HorzResolution; /* Horizontal resolution in pixels per meter */ | |
57 | + LONG VertResolution; /* Vertical resolution in pixels per meter */ | |
58 | + DWORD ColorsUsed; /* Number of colors in the image */ | |
59 | + DWORD ColorsImportant; /* Minimum number of important colors */ | |
60 | + /* Fields added for Windows 4.x follow this line */ | |
61 | + | |
62 | + DWORD RedMask; /* Mask identifying bits of red component */ | |
63 | + DWORD GreenMask; /* Mask identifying bits of green component */ | |
64 | + DWORD BlueMask; /* Mask identifying bits of blue component */ | |
65 | + DWORD AlphaMask; /* Mask identifying bits of alpha component */ | |
66 | + DWORD CSType; /* Color space type */ | |
67 | + LONG RedX; /* X coordinate of red endpoint */ | |
68 | + LONG RedY; /* Y coordinate of red endpoint */ | |
69 | + LONG RedZ; /* Z coordinate of red endpoint */ | |
70 | + LONG GreenX; /* X coordinate of green endpoint */ | |
71 | + LONG GreenY; /* Y coordinate of green endpoint */ | |
72 | + LONG GreenZ; /* Z coordinate of green endpoint */ | |
73 | + LONG BlueX; /* X coordinate of blue endpoint */ | |
74 | + LONG BlueY; /* Y coordinate of blue endpoint */ | |
75 | + LONG BlueZ; /* Z coordinate of blue endpoint */ | |
76 | + DWORD GammaRed; /* Gamma red coordinate scale value */ | |
77 | + DWORD GammaGreen; /* Gamma green coordinate scale value */ | |
78 | + DWORD GammaBlue; /* Gamma blue coordinate scale value */ | |
79 | + } WIN4XBITMAPHEADER; | |
80 | + | |
81 | + typedef struct { | |
82 | + DWORD bV5Size; | |
83 | + LONG bV5Width; | |
84 | + LONG bV5Height; | |
85 | + WORD bV5Planes; | |
86 | + WORD bV5BitCount; | |
87 | + DWORD bV5Compression; | |
88 | + DWORD bV5SizeImage; | |
89 | + LONG bV5XPelsPerMeter; | |
90 | + LONG bV5YPelsPerMeter; | |
91 | + DWORD bV5ClrUsed; | |
92 | + DWORD bV5ClrImportant; | |
93 | + DWORD bV5RedMask; | |
94 | + DWORD bV5GreenMask; | |
95 | + DWORD bV5BlueMask; | |
96 | + DWORD bV5AlphaMask; | |
97 | + DWORD bV5CSType; | |
98 | + LONG RedX; /* X coordinate of red endpoint */ | |
99 | + LONG RedY; /* Y coordinate of red endpoint */ | |
100 | + LONG RedZ; /* Z coordinate of red endpoint */ | |
101 | + LONG GreenX; /* X coordinate of green endpoint */ | |
102 | + LONG GreenY; /* Y coordinate of green endpoint */ | |
103 | + LONG GreenZ; /* Z coordinate of green endpoint */ | |
104 | + LONG BlueX; /* X coordinate of blue endpoint */ | |
105 | + LONG BlueY; /* Y coordinate of blue endpoint */ | |
106 | + LONG BlueZ; /* Z coordinate of blue endpoint */ | |
107 | + DWORD bV5GammaRed; | |
108 | + DWORD bV5GammaGreen; | |
109 | + DWORD bV5GammaBlue; | |
110 | + DWORD bV5Intent; | |
111 | + DWORD bV5ProfileData; | |
112 | + DWORD bV5ProfileSize; | |
113 | + DWORD bV5Reserved; | |
114 | + } BITMAPV5HEADER, *PBITMAPV5HEADER; | |
115 | + | |
116 | + | |
117 | + //compression methods | |
118 | + const unsigned int BI_RGB = 0; | |
119 | + const unsigned int BI_BITFIELDS = 3; | |
120 | + | |
121 | + class bmp { | |
122 | + std::ifstream file; | |
123 | + public: | |
124 | + unsigned int dib_header_size; | |
125 | + size_t bit_pos; // start position (relative to the beginning of the file) of the bitmap bits | |
126 | + size_t total_size; //total size of the bitmap file (in bytes) | |
127 | + size_t width; | |
128 | + size_t height; | |
129 | + int channels; | |
130 | + int bits_per_pixel; | |
131 | + unsigned int compression; | |
132 | + | |
133 | + size_t bytes() { | |
134 | + return width * height * bits_per_pixel / 8; | |
135 | + } | |
136 | + void read_bmpFileHeader() { | |
137 | + BITMAPFILEHEADER file_header; | |
138 | + file.read((char*)&file_header, sizeof(BITMAPFILEHEADER)); | |
139 | + bit_pos = file_header.bfOffBits; | |
140 | + total_size = file_header.bfSize; | |
141 | + } | |
142 | + void read_bmpCoreHeader() { | |
143 | + tagBITMAPCOREHEADER header; | |
144 | + file.read((char*)&header, sizeof(tagBITMAPCOREHEADER)); | |
145 | + width = header.bcWidth; | |
146 | + height = header.bcHeight; | |
147 | + bits_per_pixel = header.bcBitCount; | |
148 | + compression = 0; | |
149 | + } | |
150 | + void read_bmpInfoHeader() { | |
151 | + tagBITMAPINFOHEADER info_header; | |
152 | + file.read((char*)&info_header, sizeof(tagBITMAPINFOHEADER)); | |
153 | + width = info_header.biWidth; | |
154 | + height = info_header.biHeight; | |
155 | + bits_per_pixel = info_header.biBitCount; | |
156 | + compression = info_header.biCompression; | |
157 | + } | |
158 | + void read_bmpV4Header() { | |
159 | + WIN4XBITMAPHEADER header; | |
160 | + file.read((char*)&header, sizeof(WIN4XBITMAPHEADER)); | |
161 | + width = header.Width; | |
162 | + height = header.Height; | |
163 | + bits_per_pixel = header.BitsPerPixel; | |
164 | + compression = header.Compression; | |
165 | + } | |
166 | + void read_bmpV5Header() { | |
167 | + BITMAPV5HEADER header; | |
168 | + file.read((char*)&header, sizeof(BITMAPV5HEADER)); | |
169 | + width = header.bV5Width; | |
170 | + height = header.bV5Height; | |
171 | + bits_per_pixel = header.bV5BitCount; | |
172 | + compression = header.bV5Compression; | |
173 | + } | |
174 | + void read_dib() { //read the bitmap DIB information header | |
175 | + int header_pos = file.tellg(); | |
176 | + file.read((char*)&dib_header_size, sizeof(unsigned int)); | |
177 | + file.seekg(header_pos); | |
178 | + switch (dib_header_size) { | |
179 | + case DIB_BITMAPCOREHEADER: read_bmpCoreHeader(); break; | |
180 | + case DIB_BITMAPINFOHEADER: read_bmpInfoHeader(); break; | |
181 | + case DIB_BITMAPV4HEADER: read_bmpV4Header(); break; | |
182 | + case DIB_BITMAPV5HEADER: read_bmpV5Header(); break; | |
183 | + default: | |
184 | + std::cout << "stim::bmp ERROR: this bitmap header format isn't supported" << std::endl; | |
185 | + exit(1); | |
186 | + } | |
187 | + } | |
188 | + | |
189 | + bool open(std::string filename) { //open the bitmap file and read the header data | |
190 | + file.open(filename, std::ifstream::binary); | |
191 | + if (!file) { | |
192 | + std::cout << "stim::bmp ERROR: error opening file: " << filename.c_str() << std::endl; | |
193 | + return false; | |
194 | + } | |
195 | + read_bmpFileHeader(); //read the file header | |
196 | + read_dib(); | |
197 | + if (compression != BI_RGB) { //check for compression | |
198 | + std::cout << "stim::bmp ERROR: this file is compressed, and compression is not supported" << std::endl; | |
199 | + return false; | |
200 | + } | |
201 | + return true; | |
202 | + } | |
203 | + void close() { | |
204 | + file.close(); | |
205 | + } | |
206 | + | |
207 | + /// Copy the bitmap data into a pre-allocated array | |
208 | + bool read(char* dst){ | |
209 | + file.seekg(bit_pos); //seek to the beginning of the data array | |
210 | + size_t row_bytes = width * bits_per_pixel / 8; //number of bytes in each row | |
211 | + size_t padding = row_bytes % 4; //calculate the padding on disk for each row (rows must be multiples of 4) | |
212 | + | |
213 | + if(file){ | |
214 | + for (size_t h = 0; h < height; h++) { //for each row in the image | |
215 | + file.read(dst + (height - h - 1) * row_bytes, row_bytes); //read the row of image data | |
216 | + file.seekg(padding, std::ios::cur); //seek to the end of the row on disk | |
217 | + if (file.eof()) std::cout << "stim::bmp ERROR: array size incorrect, end of file reached while reading bitmap." << std::endl; | |
218 | + else if (file.fail()) std::cout << "stim::bmp ERROR: reading bitmap array failed." << std::endl; | |
219 | + else if (file.bad()) std::cout << "stim::bmp ERROR: stream integrity failed while reading bitmap array" << std::endl; | |
220 | + } | |
221 | + return true; | |
222 | + } | |
223 | + else{ | |
224 | + std::cout<<"stim::bmp ERROR: could not read array from file."<<std::endl; | |
225 | + return false; | |
226 | + } | |
227 | + } | |
228 | + }; | |
229 | + | |
230 | + bool save_bmp(std::string filename, char* bits, size_t width, size_t height) { | |
231 | + size_t bits_per_pixel = 24; | |
232 | + size_t row_bytes = width * bits_per_pixel / 8; //number of bytes in each row | |
233 | + size_t padding = row_bytes % 4; //calculate the padding on disk for each row (rows must be multiples of 4) | |
234 | + | |
235 | + tagBITMAPFILEHEADER file_header; | |
236 | + memset(&file_header, 0, sizeof(tagBITMAPFILEHEADER)); //initialize the file header structure to zero | |
237 | + file_header.bfOffBits = sizeof(tagBITMAPFILEHEADER) + sizeof(tagBITMAPCOREHEADER); //the offset includes both the file and DIB header | |
238 | + size_t bytes = width * height * 3; | |
239 | + file_header.bfSize = file_header.bfOffBits + (row_bytes + padding) * height; //calculate the size of the bitmap file | |
240 | + file_header.bfType = 0x4D42; | |
241 | + | |
242 | + tagBITMAPCOREHEADER info_header; | |
243 | + memset(&info_header, 0, sizeof(tagBITMAPCOREHEADER)); //initialize the info header to zero | |
244 | + info_header.bcBitCount = bits_per_pixel; | |
245 | + info_header.bcHeight = height; | |
246 | + info_header.bcWidth = width; | |
247 | + info_header.bcSize = sizeof(tagBITMAPCOREHEADER); | |
248 | + info_header.bcPlanes = 1; | |
249 | + | |
250 | + std::ofstream outfile(filename, std::ios::binary); //open the output file for binary writing | |
251 | + outfile.write((char*)&file_header, sizeof(tagBITMAPFILEHEADER)); //write the file header | |
252 | + outfile.write((char*)&info_header, sizeof(tagBITMAPCOREHEADER)); //write the information header | |
253 | + | |
254 | + char* pad = (char*)malloc(padding); //create a buffer that will be written as padding | |
255 | + memset(pad, 0, padding); | |
256 | + for (size_t h = 0; h < height; h++) { | |
257 | + outfile.write((char*)(bits + (height - h - 1) * row_bytes), row_bytes); //write the bitmap data | |
258 | + outfile.write(pad, padding); | |
259 | + } | |
260 | + free(pad); | |
261 | + return true; | |
262 | + } | |
263 | +} | |
0 | 264 | \ No newline at end of file | ... | ... |
stim/image/image.h
... | ... | @@ -13,6 +13,9 @@ |
13 | 13 | #include <fstream> |
14 | 14 | #include <cstring> |
15 | 15 | |
16 | +#include <stim/image/bmp.h> | |
17 | +#include <stim/parser/filename.h> | |
18 | + | |
16 | 19 | namespace stim{ |
17 | 20 | /// This static class provides the STIM interface for loading, saving, and storing 2D images. |
18 | 21 | /// Data is stored in an interleaved (BIP) format (default for saving and loading is RGB). |
... | ... | @@ -130,6 +133,17 @@ public: |
130 | 133 | return *this; |
131 | 134 | } |
132 | 135 | |
136 | + void load_bmp(std::string filename) { | |
137 | + stim::bmp bitmap; | |
138 | + bitmap.open(filename); //load the bitmap and read the headers | |
139 | + resize(bitmap.width, bitmap.height, 3); //resize the current image to match the bitmap | |
140 | + if (!bitmap.read((char*)img)) { //read the bits from file | |
141 | + std::cout << "stim::image ERROR: problem loading bitmap image." << std::endl; | |
142 | + exit(1); | |
143 | + } | |
144 | + bitmap.close(); //close the bitmap file | |
145 | + } | |
146 | + | |
133 | 147 | //save a Netpbm file |
134 | 148 | void load_netpbm(std::string filename) { |
135 | 149 | std::ifstream infile(filename.c_str(), std::ios::in | std::ios::binary); //open an output file |
... | ... | @@ -226,7 +240,11 @@ public: |
226 | 240 | if(C() == 3) //if this is a 3-color image, OpenCV uses BGR interleaving |
227 | 241 | from_opencv(cv_ptr, X(), Y()); |
228 | 242 | #else |
229 | - load_netpbm(filename); | |
243 | + stim::filename file(filename); | |
244 | + if (file.extension() == "ppm") | |
245 | + load_netpbm(filename); | |
246 | + else if (file.extension() == "bmp") | |
247 | + load_bmp(filename); | |
230 | 248 | #endif |
231 | 249 | } |
232 | 250 | |
... | ... | @@ -258,6 +276,10 @@ public: |
258 | 276 | outfile.close(); |
259 | 277 | } |
260 | 278 | |
279 | + void save_bmp(std::string filename) { | |
280 | + stim::save_bmp(filename, (char*)img, width(), height()); | |
281 | + } | |
282 | + | |
261 | 283 | //save a file |
262 | 284 | void save(std::string filename){ |
263 | 285 | #ifdef USING_OPENCV |
... | ... | @@ -272,7 +294,15 @@ public: |
272 | 294 | cv::imwrite(filename, cvImage); |
273 | 295 | free(buffer); |
274 | 296 | #else |
275 | - save_netpbm(filename); | |
297 | + stim::filename file(filename); | |
298 | + if (file.extension() == "ppm") | |
299 | + save_netpbm(filename); | |
300 | + else if (file.extension() == "bmp") | |
301 | + save_bmp(filename); | |
302 | + else { | |
303 | + std::cout << "stim::image ERROR: File type not supported without OpenCV. Make sure to link OpenCV and define USING_OPENCV" << std::endl; | |
304 | + exit(1); | |
305 | + } | |
276 | 306 | #endif |
277 | 307 | } |
278 | 308 | |
... | ... | @@ -399,8 +429,8 @@ public: |
399 | 429 | |
400 | 430 | image<T> result = *this; //create a new image for output |
401 | 431 | size_t N = size(); //get the number of values in the image |
402 | - double range = maxval - minval; //calculate the current range of the image | |
403 | - double desired_range = high - low; //calculate the desired range of the image | |
432 | + T range = maxval - minval; //calculate the current range of the image | |
433 | + T desired_range = high - low; //calculate the desired range of the image | |
404 | 434 | for (size_t n = 0; n < N; n++) { //for each element in the image |
405 | 435 | result.data()[n] = desired_range * (img[n] - minval) / range + low; |
406 | 436 | } | ... | ... |