I want to read and write a ppm image and calculate the average number of color.
My read method works fine and also the average number of each color is correct, but my write method gives me a wrong result.
The buffer which holds the data has type Vec3<float>*. Every 3D vector holds the r, g and b values.
Here is the read method:
Image * ReadPPM(const char * filename) {
Image* image;
ifstream file;
unsigned int width, height;
string version;
float maxvalue;
file.open(filename, ios::in | ios::binary);
if (!file) {
cerr << "file could not be open" << endl;
exit(EXIT_FAILURE);
}
// read the header
file >> version;
// check the header
// PPM "header" is valid
if (version.compare("P6") != 0)
{
cout << "Invalid image format (must be 'P6')";
exit(EXIT_FAILURE);
}
file >> width >> height >> maxvalue;
size_t size = height * width*3;
unsigned char * buffer = new unsigned char[size];
Vec3<float>* finalBuffer = new Vec3<float>[size / 3];
file.get();
file.read( (char *)buffer, size);
int j = 0;
for (int i = 0; i < size; i+=3) {
Vec3<float> vec(
(int)buffer[i]/maxvalue,
(int)buffer[i+1] / maxvalue,
(int)buffer[i+2] / maxvalue
);
finalBuffer[j] = vec;
j++;
}
file.clear();
file.close();
image = new Image(width, height, finalBuffer);
return image;
}
Here is the write method:
bool Image::operator >> (string filename) {
ofstream file;
file.open(filename,ios::out | ios::binary);
if (!file) {
cerr << "Cannot open file" << endl;
return false;
}
file << "P6" <<"\n";
file << getWidth() << "\n";
file << getHeight() << "\n";
file << "255" << "\n";
size_t size = height * width;
void*img = getRawDataPtr(); //returns void* buffer
unsigned char * temp = new unsigned char[size*3];
Vec3<float>* buff = (Vec3<float>*)img;
for (int i = 0; i<size; i++) {
Vec3<float> vector;
vector[0] =buff[i].x;
vector[1] = buff[i].y;
vector[2] = buff[i].z; // /255.f;
buff[i] = vector;
}
for (int i = 0; i < size; i++) {
temp[i * 3] = static_cast<unsigned char>(buff[i].x);
temp[i * 3 + 1] = static_cast<unsigned char>(buff[i].y);
temp[i * 3 + 2] = static_cast<unsigned char>(buff[i].z);
}
file.write((char *)temp, size * 3);
if (file.fail()) {
cerr << "Could not write data" << endl;
return false;
}
file.clear();
file.close();
return true;
delete[] buff;
delete[] temp;
}
Your values are normalized between 0 and 1 when reading the file. When writing, you just cast the float values into unsigned char, so you can only obtain 0 and 1 values: I guess you final image is filled with black.
void*img = getRawDataPtr(); //returns void* buffer
unsigned char * temp = new unsigned char[size*3];
Vec3<float>* buff = (Vec3<float>*)img;
Why creating a dynamic array you will have to manage? You are coding in C++, so use a container like std::vector.
for (int i = 0; i<size; i++) {
Vec3<float> vector;
vector[0] =buff[i].x;
vector[1] = buff[i].y;
vector[2] = buff[i].z; // /255.f;
buff[i] = vector;
}
What is the purpose of this loop? You are creating a Vec3<float> filled without transformation from the Vec3<float> in the current pixel in which you assign without transformation your temporary value: this just does nothing.
return true;
delete[] buff;
delete[] temp;
As I said in the comments, the code after return is not executed, so you will never release the memory you allocated for temp. Use a std::vector and you will not have to do it. buff is your original image: you should not delete it here.
Once cleaned, your code may become:
std::vector<unsigned char> temp(size*3);
Vec3<float>* buff = static_cast<Vec3<float>*>(getRawDataPtr());
for (int i = 0; i < size; i++) {
temp[i * 3] = static_cast<unsigned char>(buff[i].x * 255);
temp[i * 3 + 1] = static_cast<unsigned char>(buff[i].y * 255);
temp[i * 3 + 2] = static_cast<unsigned char>(buff[i].z * 255);
}
file.write(reinterpret_cast<char*>(&temp[0]), size * 3);
if (file.fail()) {
cerr << "Could not write data" << endl;
return false;
}
file.close();
return true;
Related
i have found this Loading a tga/bmp file in C++/OpenGL, it works flawless and would like to use it in my project. however my project requires a RGB struct to modify the pixels buffer.
glDrawPixels(win_height, win_width, GL_RGB, GL_FLOAT, myBuffer);
it needs some changes to make it work in my project.
typedef union PixelInfo
{
std::uint32_t Colour;
struct
{
float b, g, r, a;
};
} *PPixelInfo;
class BMP
{
private:
std::uint32_t width, height;
std::uint16_t BitsPerPixel;
std::vector<std::uint8_t> Pixels;
public:
BMP(const char* FilePath);
std::vector<std::uint8_t> GetPixels() const {return this->Pixels;}
std::uint32_t GetWidth() const {return this->width;}
std::uint32_t GetHeight() const {return this->height;}
bool HasAlphaChannel() {return BitsPerPixel == 32;}
};
BMP::BMP(const char* FilePath)
{
std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
if (!hFile.is_open()) throw std::invalid_argument("Error: File Not Found.");
hFile.seekg(0, std::ios::end);
std::size_t Length = hFile.tellg();
hFile.seekg(0, std::ios::beg);
std::vector<std::uint8_t> FileInfo(Length);
hFile.read(reinterpret_cast<char*>(FileInfo.data()), 54);
if(FileInfo[0] != 'B' && FileInfo[1] != 'M')
{
hFile.close();
throw std::invalid_argument("Error: Invalid File Format. Bitmap Required.");
}
if (FileInfo[28] != 24 && FileInfo[28] != 32)
{
hFile.close();
throw std::invalid_argument("Error: Invalid File Format. 24 or 32 bit Image Required.");
}
BitsPerPixel = FileInfo[28];
width = FileInfo[18] + (FileInfo[19] << 8);
height = FileInfo[22] + (FileInfo[23] << 8);
std::uint32_t PixelsOffset = FileInfo[10] + (FileInfo[11] << 8);
std::uint32_t size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
Pixels.resize(size);
hFile.seekg (PixelsOffset, std::ios::beg);
hFile.read(reinterpret_cast<char*>(Pixels.data()), size);
hFile.close();
}
int main()
{
BMP info = BMP("Fixedsys16x28.bmp");
//info.
std::cout << "getwidth: " << info.GetWidth() << endl;
std::cout << "getheight: " << info.GetHeight()<< endl;
// for(auto i : info.GetPixels()){
// //float i_float = float(i);
// //float res = i_float / 15.0;
// //std::cout << std::hex << (int)i << " " << endl;
// std::cout << setprecision(2) << fixed << (float) i / 255 << " " << endl;
// }
std::cout << endl;
std::cout << "size: " << info.GetPixels().size() << endl;
std::cout << "alpha: " << info.HasAlphaChannel() << endl;
std::cout << setprecision(2) << fixed;
for(int i = 0; i < static_cast<int>(info.GetPixels().size()); i++){
for(int j = 0; j < 3; j++){
if(j == 0){
std::cout << (float) info.GetPixels()[i]/ 255 << " ";
}
else if(j == 1){
std::cout << (float) info.GetPixels()[i] / 255 << " ";
}
else{
std::cout << (float) info.GetPixels()[i] / 255;
}
}
std::cout << endl;
}
getchar();
GLuint texture = 0;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, info.HasAlphaChannel() ? GL_RGBA : GL_RGB, info.GetWidth(), info.GetWidth(), 0, info.HasAlphaChannel() ? GL_BGRA : GL_BGR, GL_UNSIGNED_BYTE, info.GetPixels().data());
}
how to change this line of code?
hFile.read(reinterpret_cast<char*>(Pixels.data()), size);
so that vector gets filed with ...
typedef union PixelInfo
{
std::uint32_t Colour;
struct
{
float B, G, R, A;
};
} *PPixelInfo;
collections.
after changing the setup of vector off course or use another.
what kind of of method / function, could replace Pixels.data() , so it collect these 4 bytes and assembles them accordingly in the type of struct PixelInfo?
since Pixels.data() basically provide a pointer to the pixel vector.
how does a method that can perform the requested task look like?
collect 4 bytes of data and format them accordingly and put them in a vector<PixelInfo> while converting "std::uint8_t" into float. fast and efficiently.
it should take this in consideration "info.HasAlphaChannel()".
the accompanied image data is:
512×84 and the pixel vector size is "129024".
43008×3 bytes = 129024.
i made this loop on the pixel vector:
std::cout << setprecision(2) << fixed;
for(int i = 0; i < static_cast<int>(info.GetPixels().size()); i++){
for(int j = 0; j < 3; j++){
if(j == 0){
std::cout << (float) info.GetPixels()[i]/ 255 << " ";
}
else if(j == 1){
std::cout << (float) info.GetPixels()[i] / 255 << " ";
}
else{
std::cout << (float) info.GetPixels()[i] / 255;
}
}
std::cout << endl;
}
it seams to have worked correctly and even the last line contains 3 "bytes" of data.
this routine takes, like 20 seconds for an image 400 x 400 px.
vector<PixelInfo> pixelStore (info->GetPixels().size() / 3);
int counter = 0;
for(int i = 0; i < size; i+=3){
PixelInfo temp;
temp.r = 0;
temp.g = 0;
temp.b = 0;
for(int j = 0; j < 3; j++){
if(j == 0){
temp.r = info->GetPixels()[i + j];
}
else if(j == 1){
temp.g = info->GetPixels()[i + j];
}
else{
temp.b = info->GetPixels()[i + j];
}
if(j == 2){
//pixelStore.push_back(temp);
pixelStore[counter] = temp;
counter++;
}
}
}
Your code shows std::vector<std::uint8_t> Pixels; which is a sequence of 8-bit pixels I suppose. You want a vector of PixelInfo instead? Now does that structure match the format of bytes in the file? If that's the case, then what's the problem?
If the bytes in the file match the layout of your structure (including the machine's Endian!) just do it the same way:
vector<PixelInfo> Pixels;
size_t pixels_to_read = ....??? ;
Pixels.resize(pixels_to_read);
hFile.read (reinterpret_cast<char*>(Pixels.data()), pixels_to_read*sizeof(PixelInfo));
update
collect 4 bytes of data and format them accordingly and put them in a vector while converting "std::uint8_t" into float. fast and efficiently.
std::byte buf[4];
hFile.read (reinterpret_cast<char*>(buf),4); // read 4 bytes from file
PixelInfo px;
px.R = buf[0]; // converts 8-bit integer to float
px.G = buf[1];
px.B = buf[2];
px.A = buf[3];
Note that your PixelInfo union doesn't make sense -- how does the 32-bit color match the layout of the 4 32-bit components? Furthermore, I don't think that definition is legal, as there is no such thing as an anonymous struct. If that compiles, it must be a compiler extension.
As for doing it fast :
Read in a lot at a time, rather than one pixel. A whole row is good, if it's not practical to read the entire file into memory. Then you just step along the buffer you had loaded.
You can hope that the 4 individual statements get auto-vectorized by the optimizer. If you have a tight and simple loop around just that part (to process a whole row) you might get the compiler to vectorize it.
The fact that you have to write 4 separate statements because the left hand side uses different names rather than an index should not affect the overall speed.
20 seconds for a 400×400 pixel image? I would think it would be instantaneous. I have experience loading/converting/decoding graphics files, having written a library for DOS and bare metal (16-bit x86 code) back in the early '90's. You might try benchmarking code that only does this conversion listed above, to see if that's your bottleneck or not. Look at the code generated (in the debugger, or using Compiler Explorer) to see if it's vectorizing. Look into calling the vector primitives intrinsic instructions directly, if your compiler has that available. See if you're using all the right compiler optimization flags.
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
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").
When I compile my program I get this unhandled exception that I don't understand or know how to fix.
Exception thrown at 0x00007FF6DFF937FE in My
Code.exe: 0xC0000005: Access violation writing location
0x000002E07396F000.
If there is a handler for this exception, the program may be safely
continued.
//main.cpp
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <vector>
#include <string>
#include "Image.h"
Image readPPM(const char* file);
void writePPM(const Image &img, const char* file);
int main()
{
std::vector<Image> inputImages;
inputImages.resize(13);
Image *outputImage;
for (int x = 1; x < 14; x++)
{
inputImages.push_back(Image(3264, 2448));
inputImages[x] = readPPM(std::string("Images/ImageStacker_set1/IMG_" + std::to_string(x) + ".ppm").c_str());
}
outputImage = new Image(3264, 2448);
outputImage->pixels = new Image::Rgb[3264, 2448];
for (int x = 0; x < 3264 * 2448; x++) {
float sumR = 0.f;
float sumG = 0.f;
float sumB = 0.f;
for (int i = 0; i < 12; i++) {
sumR += inputImages[i].pixels[x].r;
sumG += inputImages[i].pixels[x].g;
sumB += inputImages[i].pixels[x].b;
}
outputImage->pixels[x].r = sumR / 13;
outputImage->pixels[x].g = sumG / 13;
outputImage->pixels[x].b = sumB / 13;
}
writePPM(*outputImage, "testPPM.ppm");
return 0;
}
Image readPPM(const char *filename)
{
//Remove this cout to prevent multiple outputs
std::cout << "Reading image ..." << std::endl;
std::ifstream ifs;
ifs.open(filename, std::ios::binary);
Image src;
try {
if (ifs.fail()) {
throw("Can't open the input file - is it named correctly/is it in the right directory?");
}
std::string header;
int w, h, b;
ifs >> header;
if (strcmp(header.c_str(), "P6") != 0) throw("Can't read the input file - is it in binary format (Has P6 in the header)?");
ifs >> w >> h >> b;
src.w = w;
src.h = h;
//std::cout << w << " " << h << std::endl;
src.pixels = new Image::Rgb[w * h]; // this is throw an exception if bad_alloc
ifs.ignore(256, '\n'); // skip empty lines in necessary until we get to the binary data
unsigned char pix[3]; // read each pixel one by one and convert bytes to floats
for (int i = 0; i < w * h; ++i) {
ifs.read(reinterpret_cast<char *>(pix), 3);
src.pixels[i].r = pix[0] / 255.f;
src.pixels[i].g = pix[1] / 255.f;
src.pixels[i].b = pix[2] / 255.f;
}
ifs.close();
}
catch (const char *err) {
fprintf(stderr, "%s\n", err);
ifs.close();
}
//Confirm image read
//Delete this to prevent multiple lines output
std::cout << "Image read" << std::endl;
return src;
}
//Write data out to a ppm file
//Constructs the header as above
void writePPM(const Image &img, const char *filename)
{
//std::cout << filename << std::endl;
std::cout << "Writing image ..." << std::endl;
if (img.w == 0 || img.h == 0) { fprintf(stderr, "Can't save an empty image\n"); return; }
std::ofstream ofs;
try {
ofs.open(filename, std::ios::binary); // need to spec. binary mode for Windows users
if (ofs.fail()) throw("Can't open output file");
ofs << "P6\n" << img.w << " " << img.h << "\n255\n";
//std::cout << "P6\n" << img.w << " " << img.h << "\n255\n";
unsigned char r, g, b;
// loop over each pixel in the image, clamp and convert to byte format
for (int i = 0; i < img.w * img.h; ++i) {
r = static_cast<unsigned char>(std::min(1.f, img.pixels[i].r) * 255);
g = static_cast<unsigned char>(std::min(1.f, img.pixels[i].g) * 255);
b = static_cast<unsigned char>(std::min(1.f, img.pixels[i].b) * 255);
ofs << r << g << b;
}
ofs.close();
//Confirm image write
std::cout << "Image written" << std::endl;
}
catch (const char *err) {
fprintf(stderr, "%s\n", err);
ofs.close();
}
}
Image.h
#pragma once
//*********************************************
//Image class to hold and allow manipulation of images once read into the
code
//from https://www.scratchapixel.com/
//*********************************************
#include <cstdlib>
#include <cstdio>
class Image
{
public:
// Rgb structure, i.e. a pixel
struct Rgb
{
Rgb() : r(0), g(0), b(0) {}
Rgb(float c) : r(c), g(c), b(c) {}
Rgb(float _r, float _g, float _b) : r(_r), g(_g), b(_b) {}
bool operator != (const Rgb &c) const
{
return c.r != r || c.g != g || c.b != b;
}
Rgb& operator *= (const Rgb &rgb)
{
r *= rgb.r, g *= rgb.g, b *= rgb.b; return *this;
}
Rgb& operator += (const Rgb &rgb)
{
r += rgb.r, g += rgb.g, b += rgb.b; return *this;
}
friend float& operator += (float &f, const Rgb rgb)
{
f += (rgb.r + rgb.g + rgb.b) / 3.f; return f;
}
float r, g, b;
};
Image() : w(0), h(0), pixels(nullptr) { /* empty image */ }
Image(const unsigned int &_w, const unsigned int &_h, const Rgb &c =
kBlack) :
w(_w), h(_h), pixels(NULL)
{
pixels = new Rgb[w * h];
for (int i = 0; i < w * h; ++i)
pixels[i] = c;
}
//copy constructor
Image(const Image &im)
{
w = im.w;
h = im.h;
pixels = new Rgb[im.w * im.h];
for (int i = 0; i < im.w * im.h; ++i)
pixels[i] = im.pixels[i];
}
//copy assignment operator
Image& operator=(const Image& other)
{
w = other.w;
h = other.h;
pixels = new Rgb[other.w * other.h];
for (int i = 0; i < other.w * other.h; ++i)
pixels[i] = other.pixels[i];
return *this;
}
const Rgb& operator [] (const unsigned int &i) const
{
return pixels[i];
}
Rgb& operator [] (const unsigned int &i)
{
return pixels[i];
}
~Image()
{
if (pixels != NULL) delete[] pixels;
//delete[] pixels;
}
//unsigned int w, h; // Image resolution
int w, h; // Image resolution
Rgb *pixels; // 1D array of pixels
static const Rgb kBlack, kWhite, kRed, kGreen, kBlue; // Preset colors
};
const Image::Rgb Image::kBlack = Image::Rgb(0);
const Image::Rgb Image::kWhite = Image::Rgb(1);
const Image::Rgb Image::kRed = Image::Rgb(1, 0, 0);
const Image::Rgb Image::kGreen = Image::Rgb(0, 1, 0);
const Image::Rgb Image::kBlue = Image::Rgb(0, 0, 1);
Here's the code that appears to be causing the problem. When debugging it seems to be that there's something wrong with the pointers here:
outputImage->pixels[x].r = sumR / 13;
outputImage->pixels[x].g = sumG / 13;
outputImage->pixels[x].b = sumB / 13;
Sorry I can't really provide anything else to go on but I have never had an error like this before so I don't know where to start in fixing it.
There's some level of bad here. But I believe at the core you meant to write:
inputImages[x] = Image(3264, 2448);
inputImages[x].pixels = readPPM(std::string("Images/ImageStacker_set1/IMG_" + std::to_string(x) + ".ppm").c_str());
Stepping through your code:
std::vector<Image> inputImages - Leaves inputImages with a size of 0
inputImages.resize(13); - Leaves inputImages with a size of 13 default constructed Images
inputImages.push_back(Image(3264, 2448)); - Increments the size of inputImages by 1 by adding a default image onto the tail end, ultimately increasing the size of inputImages to 26
inputImages[x] = readPPM(std::string("Images/ImageStacker_set1/IMG_" + std::to_string(x) + ".ppm").c_str()); - Modifies one of the default constructed Images in inputImages' [1 - 12] then finally modifies the first Image(3264, 2448) in inputImages
This means that at a minimum you have 13 Image(3264, 2448) objects that are never accessed. The 1st element that is accessed by the code in question, inputImage[0] is a default constructed Image. And unless readPPM returns an Image with an allocated pixels field, there are no Images in inputImages [1 - 12] range which can support access to their pixels field.
I am currently working on imaging processing using arrays to store R,G,B values from a 24 bit BITMAP image of width 120 and height 100 pixels.
Visual Studio 2010 is being used.
I have currently extracted the individual R,G,B values into three separate2D arrays from the 24 bit bitmap (it is assumed correct as the correct R,G,B values have been written to a text file with the right pixel count as well).
These individual R,G,B values need to be restored back into an array (either 1D or 2D), which is then written to an image file. The output should be identical to the original image.
I have tried the following but the output is currently incorrect (same width, height and memory size but colouring is incorrect).
Appreciate your guidance and feedback.
#include <iostream>
#include <fstream>
#include <windows.h>
#include <WinGDI.h>
unsigned char** Allocate2DArray(int w, int h)
{
unsigned char ** buffer = new unsigned char * [h]; // allocate the rows
unsigned char * memory_pool = new unsigned char [w*h]; // allocate memory pool
for (int i = 0; i < h; ++i)
{
buffer[i] = memory_pool; // point row pointer
memory_pool += w; // go to next row in memory pool
}
return buffer;
}
void DeAllocate2DArray(unsigned char** buffer)
{
delete [] buffer[0]; // delete the memory pool
delete [] buffer; // delete the row pointers
}
using namespace std;
int main()
{
const int width = 120;
const int height = 100;
int bytesPerPixel = 3;
unsigned char m_cHeaderData[54];
unsigned char** m_cImageData = new unsigned char* [height];
for( int i = 0; i <height; i++)
{
m_cImageData[i] = new unsigned char [width*bytesPerPixel];
}
ifstream* m_pInFile;
m_pInFile = new ifstream;
m_pInFile->open("image.bmp", ios::in | ios::binary);
m_pInFile->seekg(0, ios::beg);
m_pInFile->read(reinterpret_cast<char*>(m_cHeaderData), 54);
for(int i = 0; i <height; i++)
{
m_pInFile->read(reinterpret_cast<char*>(m_cImageData[i]), width*bytesPerPixel);
}
m_pInFile->close();
// Declare a pointer of the type you want.
// This will point to the 1D array
unsigned char* array_1D;
array_1D = new unsigned char[height*width*bytesPerPixel];
if(array_1D == NULL) return 0; // return if memory not allocated
// Copy contents from the existing 2D array
int offset = 0;
for(int j=0; j<height; j++) // traverse height (or rows)
{
offset = width * bytesPerPixel* j;
for(int i=0; i<width*bytesPerPixel; i++) // traverse width
{
array_1D[offset + i] = m_cImageData[j][i];
// update value at current (i, j)
}
}
// Declare three 2D arrays to store R,G, and B planes of image.
unsigned char**arrayR_2D, **arrayG_2D, **arrayB_2D;
arrayR_2D = Allocate2DArray(width, height);
arrayG_2D = Allocate2DArray(width, height);
arrayB_2D = Allocate2DArray(width, height);
// return if memory not allocated
if(arrayR_2D == NULL || arrayG_2D == NULL || arrayB_2D == NULL) return 0;
// Extract R,G,B planes from the existing composite 1D array
ofstream RGBdata2D;
RGBdata2D.open("RGBdata2D.txt");
int pixelCount = 0;
int offsetx = 0;
int counter = 0;
for(int j=0; j<height; j++) // traverse height (or rows)
{
offsetx = width * j * bytesPerPixel;
for(int i=0; i<width*bytesPerPixel; i+=bytesPerPixel) // width
{
arrayB_2D[j][counter] = array_1D[offsetx + i+0];
arrayG_2D[j][counter] = array_1D[offsetx + i+1];
arrayR_2D[j][counter] = array_1D[offsetx + i+2];
RGBdata2D<<"B: "<< (int)arrayB_2D[j][counter] << " G: " << (int)arrayG_2D[j][counter] << " R: " << (int)arrayR_2D[j][counter]<< endl;
pixelCount++;
++counter;
}
counter = 0;
}
RGBdata2D<<"count of pixels: "<< pixelCount << endl;
RGBdata2D.close();
//put RGB from 2D array contents back into a 1D array
offset = 0;
counter = 0;
for(int j=0; j<height; j++) // traverse height (or rows)
{
offset = width * bytesPerPixel * j;
for(int i=0; i<width*bytesPerPixel; i+=bytesPerPixel) // width
{
array_1D[offset + i+0] = arrayB_2D[j][counter++];
array_1D[offset + i+1] = arrayG_2D[j][counter++];
array_1D[offset + i+2] = arrayR_2D[j][counter++];
}
counter = 0;
}
ofstream* m_pOutFileRGB;
m_pOutFileRGB = new ofstream;
m_pOutFileRGB->open("imageCopyRGB.bmp", ios::out | ios::trunc | ios::binary);
m_pOutFileRGB->write(reinterpret_cast<char*>(m_cHeaderData), 54);
for(int i = 0; i <height; i++)
{
m_pOutFileRGB->write(reinterpret_cast<char*>(array_1D), width*bytesPerPixel);
}
m_pOutFileRGB->close();
// After complete usage, delete the memory dynamically allocated
DeAllocate2DArray(arrayR_2D);
DeAllocate2DArray(arrayG_2D);
DeAllocate2DArray(arrayB_2D);
// After complete usage, delete the memory dynamically allocated
delete[] array_1D; //delete the pointer to pointer
for(int i = 0; i <height; i++)
{
delete[] m_cImageData[i];
}
delete[] m_cImageData;
system("pause");
return 0;
}
I didn't test by myself, but at this point
for(int i=0; i<width*bytesPerPixel; i+=bytesPerPixel) // width
{
array_1D[offset + i+0] = arrayB_2D[j][counter++];
array_1D[offset + i+1] = arrayG_2D[j][counter++];
array_1D[offset + i+2] = arrayR_2D[j][counter++];
}
You inclement counter too many times, and it may lead to incorrect result.
Instead, try this:
for(int i=0; i<width*bytesPerPixel; i+=bytesPerPixel) // width
{
array_1D[offset + i+0] = arrayB_2D[j][counter];
array_1D[offset + i+1] = arrayG_2D[j][counter];
array_1D[offset + i+2] = arrayR_2D[j][counter];
counter++;
}
I'm trying to read painted 16x16 bmp, but there is no 1 pixel = 3 bits (RGB). Even if first 4-5 lines is white and the rest is black the document is still full of 255 255 255 for each pixel.
In my case I need to show this image in console by analyzing RGB layers of each pixel but have a lot of trouble with it.
int main()
{
FILE* f = fopen("image.bmp", "rb");
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
int size = 3 * width * height;
unsigned char * data = new unsigned char[size]; // allocate 3 bytes per pixel
fread(data, sizeof(unsigned char), size, f); // read the rest of the data at once
fclose(f);
for (int i = 0; i < size; i += 3)
{
unsigned char tmp = data[i];
data[i] = data[i + 2];
data[i + 2] = tmp;
}
unsigned int * byteData = new unsigned int[size];
for (int i = 0; i <= size; i++)
{
byteData[i] = (int) data[i];
}
for (int i = 0; i <= size / 3; i++)
{
cout << i << ".\t" << byteData[i] << "\t" << byteData[i + 1] << "\t" << byteData[i + 2] << endl;
}
cout << endl;
cout << "=======================" << endl;
for (int j = 0; j < width; j++)
{
cout << j + 1 << ".\t";
for (int i = 0; i < height; i++)
{
//if ((int)data[j * width + i] >= 100 && (int)data[j * width + i + 1] >= 100 && (int)data[j * width + i + 2] >= 100)
if (((int) data[j * width + i] + (int) data[j * width + i + 1] + (int) data[j * width + i + 2]) / 3 <= 170)
cout << " ";
else cout << "*";
}
cout << endl;
}
getchar();
return 0;
}
As I think problem with byte sequenses and reading memory frome garbage, but if you could explain where is leak?
The solution is next: bmp should be created with 24 bpp and top-down row order.
Correct code is for 16x16 bitmap:
#include <iostream>
using namespace std;
unsigned char* readBMP(char* filename);
int main()
{
unsigned char * data = readBMP("winLogo.bmp");
int size = 16*16*3;
unsigned int * byteData = new unsigned int[size];
for (int i = 0; i <= size; i++)
{
byteData[i] = (int)data[i];
}
int k = 0;
//uncomment to write a line of RGB values
//for (int i = 0; i < size; i += 3)
//{
// cout << k+1 << ".\t" << byteData[i] << "\t" << byteData[i + 1] << "\t" << byteData[i + 2] << endl;
// k++;
//}
for (int i = 0; i < size; i+= 3)
{
if (i % 16*3 == 0)
{
cout << endl << (i/(16*3))+1 << ".\t";
}
else {};
if (byteData[i] >= 200 && byteData[i + 1] >= 200 && byteData[i + 2] >= 200)
cout << " ";
else
cout << "#";
}
getchar();
return 0;
}
unsigned char* readBMP(char* filename)
{
int i;
FILE* f = fopen(filename, "rb");
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
int size = -3 * width * height;
unsigned char* data = new unsigned char[size]; // allocate 3 bytes per pixel
fread(data, sizeof(unsigned char), size, f); // read the rest of the data at once
fclose(f);
//BGR -> RGB
for (i = 0; i < size; i += 3)
{
unsigned char tmp = data[i];
data[i] = data[i + 2];
data[i + 2] = tmp;
}
return data;
}