0

essentially, I'm making a class that takes a BMP file for the constructor. From THIS POST I get all of the header data out of the way and then read RGB data into a vector. I then calculate the intensities as 0.25R + 0.5G + 0.25B. I put these numbers into a space-separated file, line by line. With the original above my result below and Using GNUPlot to open and plot the image gives me this result.

original

distortion

As you can see, the right side of the image is consistently being wrapped around further as the image is written to file (or somewhere before this process). I've pasted the code below, any help?

std::vector<char> MImage::readBMP(std::string const file){
static constexpr size_t HEADER_SIZE = 54;

std::ifstream bmp(file, std::ios::binary);

std::array<char, HEADER_SIZE> header;
bmp.read(header.data(), header.size());

auto fileSize = *reinterpret_cast<uint32_t*>(&header[2]);
auto dataOffset = *reinterpret_cast<uint32_t*>(&header[10]);
auto width = *reinterpret_cast<uint32_t*>(&header[18]);
auto height = *reinterpret_cast<uint32_t*>(&header[22]);
auto depth = *reinterpret_cast<uint16_t*>(&header[28]);
/*
std::cout << "fileSize: " << fileSize << std::endl;
std::cout << "dataOffset: " << dataOffset << std::endl;
std::cout << "width: " << width << std::endl;
std::cout << "height: " << height << std::endl;
std::cout << "depth: " << depth << "-bit" << std::endl;
*/
std::vector<char> img(dataOffset - HEADER_SIZE);
//bmp.read(img.data(), img.size());

auto dataSize = ((width * 3 + 3) & (~3)) * height;
img.resize(dataSize);
bmp.read(img.data(), img.size());

char temp = 0;

for (int i = dataSize - 4; i >= 0; i -= 3)
{
    temp = img[i];
    img[i] = img[i + 2];
    img[i + 2] = temp;
}


// Convert to intensity
int k = 0;
int size = (int)img.size();
for (int j = 0; k+2 < size; j++)
{
    //0.25B + 0.5G + 0.25R
    img[j] = ((abs(img[k]) >> 2) + (abs(img[k + 1]) >> 1) + (abs(img[k + 2]) >> 2));
    //OutputDebugStringA((to_string(img[j]) + "\n").c_str());
    
    k += 3;
}
img.resize(dataSize / 3);
//OutputDebugStringA((to_string(img.size()) + "\n").c_str());


int a, b, c = 0;
//Testing @img data
ofstream TestPic;
TestPic.open("testpic.txt");
for (a = 0; a < HEIGHT; a++) {
    for (b = 0; b < WIDTH; b++) {
        TestPic << (int)img[c];
        if (b < WIDTH-1) {
            TestPic << " ";
        }
        c++;
    }
    TestPic << "\n";
}
TestPic.close();

return img; }

GNUPlot command: plot [0:630] [0:354] 'testpic.txt' matrix with image pixels

  • *I then calculate the intensities as 0.25R + 0.5G + 0.25B. I put these numbers into a space-separated file, line by line* -- How about first testing if you can output exactly what you read in? This will ensure that you have at the very least the input and output parts of the code working correctly. – PaulMcKenzie Nov 10 '20 at 18:19
  • It's really easy: you should not be writing your own BMP reading function. Problem solved. But seriously: it's tricky to get it right, and serious amounts of time were spent by the library developers to work around corner cases. So, I highly suggest that you don't reimplement graphical file format I/O libraries unless your job is to develop a new BMP library for some inscrutable reason. – Kuba hasn't forgotten Monica Nov 10 '20 at 18:46

1 Answers1

0

The problem you are seeing is caused by improper data alignment. Each scanline of .bmp file must have a byte-size divisible by 4. You are calculating the input data size correctly with this line:

auto dataSize = ((width * 3 + 3) & (~3)) * height;

However, while converting the img array you do not compensate/throw away the padding at the end of each scanline.

The best advice is either to use a standard bmp loader/converter like the one in STB libraries (the stb_image.h file) or, if you want to do it yourself, allocate the other array (img2 or something like that) and iterate img array scanline by scanline, writing greyscale values to img2.

By the way, this answer, which you have mentioned, contains a solution for your exact problem (it is called the "padding fix").

Viktor Latypov
  • 14,289
  • 3
  • 40
  • 55
  • Thanks for the quick reply. I did NOT know about STB, I'll look into that if my way never ends up looking good. However I tried that "padding fix" answer and it would never load anything from the 54 byte header correctly with their implementation (for example, it loads 'width' = -858993460 and the buffer for the header only ever read out ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ.....). – mitchell ruffing Nov 10 '20 at 20:19
  • -858993460 in HEX is "-0x33333334", which is _very_ suspicious and looks like an default-uninitialized/corrupt variable ) – Viktor Latypov Nov 10 '20 at 21:14