Commit d9b2b2a850d0a6d6044fe2d01eff09e0754ff0c1

Authored by David Mayerich
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
stim/grids/grid3.h 0 → 100644
  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
... ...
stim/image/bmp.h 0 → 100644
  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 }
... ...