#include <iostream>
#include <fstream> // for file I/O
#define WIDTH 128
#define HEIGHT 128
#include <cmath>
using namespace std;
typedef unsigned char unchar;
class MImage
{
///////////////////////////////////////////////////////////////////////////////////////read
public:
void readimage()
{
imageData = new unchar*[HEIGHT]; // create new array size: height of image.
for (int i = 0; i < HEIGHT; i++)
{
imageData[i] = new unchar[WIDTH]; //create matrix.
}
//image I/O
pInFile = new ifstream;
pInFile->open("L.bmp", ios::in | ios::binary); // open fileName and read as binary.
pInFile->read(reinterpret_cast<char*>(imageHeaderData), 1078); //read bmp header data into array.
for (int i = 0; i < HEIGHT; i++)
{
pInFile->read(reinterpret_cast<char*>(imageData[i]), WIDTH); //read row into each array entry.
}
pInFile->close(); //close stream.
}
public:
void write()
{
//smoothFilter();
pOutFile = new ofstream;
pOutFile->open("output.bmp", ios::out | ios::binary);
pOutFile->write(reinterpret_cast<char*>(imageHeaderData), 1078); //write header data onto output
for (int i = 0; i < HEIGHT; i++)
{
pOutFile->write(reinterpret_cast<char*>(imageData[i]), WIDTH); // write new image data.
}
pOutFile->close(); //close stream
}
public:
ifstream* pInFile;
ofstream* pOutFile;
unchar imageHeaderData[1078]; //.bmp header data with offset 1078. unchar** imageData;
};
int main()
{
MImage abc;
abc.readimage();
abc.write();
return 0;
}
I am unable to read image in a 2d array so that i could do some processing on it. i have used the code above but saved file is giving error.
what i am doing is first reading a .bmp file 128x128 then saving it in an other .bmp file. but when i try to open output file it gives error "file is corrupted or large in size"
Hope it helps ;)
I skipped error checking but you should add it to the final code. For writing a .bmp image, wirte BITMAPFILEHEADER first, then BITMAPINFOHEADER and at the end the actual raw data
FILE* filePtr;
int error;
unsigned int count;
BITMAPFILEHEADER bitmapFileHeader;
BITMAPINFOHEADER bitmapInfoHeader;
int imageSize;
unsigned char* bitmapImage;
// Open the height map file in binary.
error = fopen_s(&filePtr, filename, "rb");
// Read in the file header.
count = fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
// Read in the bitmap info header.
count = fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
// Save the dimensions of the terrain.
Width= bitmapInfoHeader.biWidth;
Height= bitmapInfoHeader.biHeight;
// Calculate the size of the bitmap image data.
imageSize = Width* Height* 3;
// Allocate memory for the bitmap image data.
bitmapImage = new unsigned char[imageSize];
// Move to the beginning of the bitmap data.
fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);
// Read in the bitmap image data.
count = fread(bitmapImage, 1, imageSize, filePtr);
// Close the file.
error = fclose(filePtr);
Don't forget to delete bitmapImage or use std::vector instead
Related
I have a exercise. It says, that the C program should be able to read the information of a bitmap file and after that it should display the picture on console.
I have already written a code but when it does not work correctly.
When I debugged the code it looks like the heap is corrupted. I thinks I have a known glitch/mistake in ScanPixelline function.
I don't know how to fix it. Can someone help me to check it?
I am relatively new to C programming.
#include "stdafx.h"
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include "stdint.h"
#include "windows.h"
#pragma pack(1)
struct BMP
{
char Type[2]; //File type. Set to "BM".
int32_t Size; //Size in BYTES of the file.
int16_t Reserved1; //Reserved. Set to zero.
int16_t Reserved2; //Reserved. Set to zero.
int32_t OffSet; //Offset to the data.
int32_t headsize; //Size of rest of header. Set to 40.
int32_t Width; //Width of bitmap in pixels.
int32_t Height; // Height of bitmap in pixels.
int16_t Planes; //Number of Planes. Set to 1.
int16_t BitsPerPixel; //Number of Bits per pixels.
int32_t Compression; //Compression. Usually set to 0.
int32_t SizeImage; //Size in bytes of the bitmap.
int32_t XPixelsPreMeter; //Horizontal pixels per meter.
int32_t YPixelsPreMeter; //Vertical pixels per meter.
int32_t ColorsUsed; //Number of colors used.
int32_t ColorsImportant; //Number of "important" colors.
};
struct Color
{
unsigned char B;
unsigned char G;
unsigned char R;
};
struct ColorTable
{
Color *colors;
unsigned long length;
};
struct PixelArray
{
Color **pixels;
unsigned long rowCount;
unsigned long columnCount;
};
void readBMP(char *File_Name, BMP &a)
{
FILE *p = fopen(File_Name, "rb");
if (p == NULL)
{
printf("Can't open file!");
fclose(p);
return;
}
else
{
fread(&a, sizeof(BMP), 1, p);
}
fclose(p);
}
void Get_Inf(BMP a)
{
if (a.Type[0] != 'B' || a.Type[1] != 'M')
{
printf("This is not a BMP file");
}
else
{
printf("This is a BMP file\n");
printf("The size of this file is %lu bytes\n", a.Size);
printf("The witdth of this image is %lu pixels\n", a.Width);
printf("The height of this image is %lu pixels\n", a.Height);
printf("The number of bits per pixels in this image is %u\n", a.BitsPerPixel);
}
}
void scanBmpPixelLine(Color *&line, unsigned long length)
{
FILE *pointer_ = fopen("test.bmp", "rb");
line = new Color[length];
fread(line, sizeof(Color), sizeof(Color)*length, pointer_);
fclose(pointer_);
//file.read((char *)line, length * sizeof(Color));
}
void skipBmpPadding(char count)
{
FILE *pointer__ = fopen("test.bmp", "rb");
if (count == 0)
{
fclose(pointer__);
return;
}
char padding[3];
fread(&padding, sizeof(char), count, pointer__);
fclose(pointer__);
//file.read((char *)&padding, count);
}
void ReadPixelArray(BMP a, PixelArray &data)
{
FILE *pointer = fopen("test.bmp", "rb");
data.rowCount = a.Height;
data.columnCount = a.Width;
data.pixels = new Color*[data.rowCount];
char paddingCount = (4 - (a.Width * (a.BitsPerPixel / 8) % 4)) % 4;
fseek(pointer, 54, SEEK_SET);
for (int i = 0; i < data.rowCount; i++)
{
scanBmpPixelLine(data.pixels[data.rowCount - i - 1], a.Width);
skipBmpPadding(paddingCount);
}
}
void drawBmp(BMP a, PixelArray data)
{
HWND console = GetConsoleWindow();
HDC hdc = GetDC(console);
for (int i = 0; i < a.Height; i++)
for (int j = 0; j < a.Width; j++)
{
Color pixel = data.pixels[i][j];
SetPixel(hdc, j, i, RGB(pixel.R, pixel.G, pixel.B));
}
ReleaseDC(console, hdc);
}
void releaseBmpPixelArray(PixelArray data)
{
for (int i = 0; i < data.rowCount; i++)
delete[]data.pixels[i];
delete[]data.pixels;
}
int main()
{
char file_name[] = "test.bmp";
BMP a;
PixelArray data;
readBMP(file_name, a);
Get_Inf(a);
ReadPixelArray(a, data);
drawBmp(a, data);
releaseBmpPixelArray(data);
}
This function:
void scanBmpPixelLine(Color *&line, unsigned long length)
{
FILE *pointer_ = fopen("test.bmp", "rb");
line = new Color[length];
fread(line, sizeof(Color), sizeof(Color)*length, pointer_);
fclose(pointer_);
//file.read((char *)line, length * sizeof(Color));
}
For starters, the intent of the function appears to be to read one line of pixel data from the file. But instead, it's re-opening the file and reading from the beginning (where the header bytes are). I'm not sure if you are aware of that...
But the crash is a result of this line:
fread(line, sizeof(Color), sizeof(Color)*length, pointer_);
The second parameter, sizeof(Color), is the size of each element. The third parameter is the number of elements to read. The total bytes read from the file will be the multiplication of the second parameter by the third parameter. So you've redundantly multiplied by sizeof(Color) one too many times. The result is that it will overwrite the line buffer.
To fix, it should be:
fread(line, sizeof(Color), length, pointer_);
You probably want to pass the FILE* pointer obtained from your ReadPixelArray function into this function instead of re-opening the file for every line.
Another code review comment. You should just read the entire file into memory instead of redundantly opening and closing the file for each operation. Then parse the header and set a pointer to the first "line" after the header.
i am trying to read and write a bmp file in c++.output file is created but is not opening and it's size is 257kb whereas the input file is 258kb.i first read and write the 14 byte header file,40 byte imageheader file,then the 512*512 pixels,here's my code,can anyone help please
#include <iostream>
#include <fstream>
#include <cmath>
using namespace std;
int main()
{
ifstream iFile;
char ch;
iFile.open("lena.bmp",ios::binary);
ofstream oFile;
oFile.open("lena3.bmp",ios::binary);
//int headerImageHeader=54;
// int imageHeader=40;
int fs[54];
//int ihs[imageHeader];
int tfs[54];
int pixel[512][512];
if(iFile.is_open() && oFile.is_open())
{
for(int i=0;i<54;i++)
{
iFile.get(ch);
fs[i]=ch;
cout<<fs[i]<<" ";
char p;
p=fs[i];
oFile<<p;
}
for(int w=0;w<512;w++)
{
for(int h=0;h<512;h++)
{
iFile.get(ch);
pixel[w][h]=ch;
//cout<<pixel[w][h]<<;
char pi=pixel[w][h];
oFile<<pi;
}
}
oFile.close();
iFile.close();
}
else cout << "Unable to open file"<<endl;
return 0;
}
Your code has multiple problems. Basically, the pixel storage in BMP file is not that simple. It depends on the type of BMP (e.g. Monochrome Bitmap, 16 Color Bitmap, 256 Color Bitmap and 24-bit Bitmap etc).
There are two options to read one Bitmap file and write it in another Bitmap file.
a) Read each byte from the source file and write in destination file
b) Understand Bitmap storage format and write code accordingly.
For option b) good source is available at wiki.
In summary, the calculation of Pixel Array storage (size) is:
PixelArraySize = RowSize x ImageHeight
Where ImageHeight is height of image in pixel. The RowSize is calculated as:
RowSize = [(BitsPerPixelxImageWidth + 31) / 32 ]x4
I'm working on a C++ program that overlays one 8 bit depth BMP image into another one of the same format and size. The program is supposed to read both images and then for every black pixel in the overlay picture, it changes the same pixel in the original picture to white. The essentials parts of my code are shown below:
The class for a BMP picture:
class BMPImage{
int width;
int height;
int size;
unsigned char* data; //Array with all pixels
unsigned char* header; //54 byte header
void setSize();
public:
BMPImage(const char* filename);
BMPImage(unsigned char* header, unsigned char* data);
int getSize();
int getWidth();
int getHeight();
unsigned char* getData();
unsigned char* getHeader();
void createFile(char* filename);
}
Constructors to create an object from a filename or from a header and a data array:
BMPImage::BMPImage(const char* filename){
FILE* f = fopen(filename, "rb");
if(f == NULL)
throw "Argument Exception";
header= new unsigned char[54];
fread(header, sizeof(unsigned char), 54, f); // read the 54-byte header
// extract image height and width from header
setSize();
cout << endl;
cout << "Reading file " << filename << endl;
cout << " Width: " << width << endl;
cout << " Height: " << height << endl;
data = new unsigned char[size];
fread(data, sizeof(unsigned char), size, f); // read the rest of the data at once
fclose(f);
}
BMPImage::BMPImage(unsigned char* h, unsigned char* d){
header = new unsigned char[54];
for(int i=0;i<54;i++){
header[i]=h[i];
}
setSize();
data = new unsigned char[size];
for(int i=0;i<size;i++){
data[i]=d[i];
}
}
A function to create a file from a BMPImage object:
void BMPImage::createFile(char* filename){
FILE* f;
f=fopen(filename, "wb");
//write header
fwrite(header,sizeof(unsigned char), 54, f);
//write data
fwrite(data, sizeof(unsigned char), size, f);
//close file
fclose(f);
cout<<endl<<"File "<< filename <<" was successfully created."<<endl;
}
Finally, my function to overlay two images. The overlay is done so that if the pixel of "overlay" is black, the corresponding pixel in the result image will be white, else the pixel will be the same as in the "original" image.
void overlay(char* original, char* overlay, char* result){
BMPImage overlayIm(overlay);
BMPImage originalIm(original);
//Check if the pictures have the same size
if(originalIm.getHeight()!=overlayIm.getHeight() || originalIm.getWidth()!=overlayIm.getWidth())
throw "Files do not have the same size";
int size = overlayIm.getSize();
unsigned char* dataResult = new unsigned char[size];
unsigned char* dataOriginal = originalIm.getData();
unsigned char* dataOverlay = overlayIm.getData();
//If a pixel in overlay is black, change corresponding pixel in original to white
for(int i=0; i<size; i++){
if(dataOverlay[i]!=0){
dataResult[i]=dataOriginal[i];
}else{
dataResult[i]=(unsigned char)255;
}
}
//Create new file with the result
BMPImage resultIm(originalIm.getHeader(), dataResult);
resultIm.createFile(result);
cout<<endl<<"New overlay image created in "<<result <<endl;
}
Although I only check for the pixels in the overlay image, it seems that the program is checking for the pixels in both the overlay and the original image at the same time. So when a pixel is black in the original image, the pixel in the result will be white, and I just want it to be white only if the pixel in overlay image is black.
An example of my problem is shown in this picture. The pictures on the left are the arguments in the overlay function. In the right, the "desired" picture is the one that my program should generate, and the "result" is the one that my program actually generates:
I don't understand why the black pixels in the original are changed to white if I never check the pixels in my original picture when I create the result image. Can anybody point me to the mistake I'm committing so that I can solve my problem?
I have been trying to read a bmp file with ifstream, however it works fine without debugging, when I run it in debug mode it fails. At the beginning I read 54 bytes of info, to get the height and the width of the picture, which are unfortunately -858993460 in debug mode, so the whole size of my picture overflows everytime, so I get a bad allocation error. I use VS 2013, could anyone help me out with this one ?
unsigned char* readBMP(char* filename)
{
int i;
char info[54];
std::ifstream ifs(filename, std::ifstream::binary);
ifs.read(info, 54);
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
int size = 3 * width * height;
char* data = new char[size]; // allocate 3 bytes per pixel
ifs.read(data, size);
ifs.close();
return (unsigned char*)data;
}
I guess you failed to open the file, so your read must been failed.
you can check: if (ifs.is_open()) { /* good*/}
you can also check: if(ifs.read(...)){/*good*/}
try this code:
unsigned char* readBMP(char* filename)
{
int i;
char info[54];
std::ifstream ifs(filename, std::ifstream::binary);
if(!ifs.is_open()){
std::cerr<<" failed to open file"<<std::endl;
return NULL;
}
if(!ifs.read(info, 54)) {
std::cerr<<" failed to read from file"<<std::endl;
return NULL;
}
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
int size = 3 * width * height;
char* data = new char[size]; // allocate 3 bytes per pixel
ifs.read(data, size);
ifs.close();
return (unsigned char*)data;
}
I have a jpeg image in buffer jpegBuffer. I'm trying to pass it to cv::imdecode function:
Mat matrixJprg = imdecode(Mat(jpegBuffer), 1);
I get this error:
/home/richard/Desktop/richard/client/src/main.cc:108: error: no matching function for call to ‘cv::Mat::Mat(char*&)’
This is how I fill jpegBuffer:
FILE* pFile;
long lSize;
char * jpegBuffer;
pFile = fopen ("img.jpg", "rb");
if (pFile == NULL)
{
exit (1);
}
// obtain file size.
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);
// allocate memory to contain the whole file.
jpegBuffer = (char*) malloc (lSize);
if (jpegBuffer == NULL)
{
exit (2);
}
// copy the file into the buffer.
fread (jpegBuffer, 1, lSize, pFile);
// terminate
fclose (pFile);
I had to do the-same thing and my image data was already in char array format and was arriving from a network and a plugin source. The current answer shows how to do this but it requires copying the data into a vector first which is a waste of time and resources.
This is possible to do directly without creating a copy of it. You were so close with your imdecode(Mat(jpegBuffer), 1); code in your question.
You need to use the constructor overload for the Mat class below:
Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP);
To create this Mat, pass 1 to the rows, the size of the array to the cols, CV_8UC1 to the type and the char array itself to the data param. Pass this Mat to the cv::imdecode function with the mat as the first param and CV_LOAD_IMAGE_UNCHANGED as the second param.
Basic example:
char *buffer = dataFromNetwork;
int bufferLength = sizeOfDataFromNetwork;
cv::Mat matImg;
matImg = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, buffer), CV_LOAD_IMAGE_UNCHANGED);
Complete Example (Reads file named "test.jpg" into char array and uses imdecode to decode the data from the char array then displays it):
#include <iostream>
#include <fstream>
#include <opencv2/highgui.hpp>
#include <string>
using namespace std;
int main()
{
// Open image file to read from
char imgPath[] = "./test.jpg";
ifstream fileImg(imgPath, ios::binary);
fileImg.seekg(0, std::ios::end);
int bufferLength = fileImg.tellg();
fileImg.seekg(0, std::ios::beg);
if (fileImg.fail())
{
cout << "Failed to read image" << endl;
cin.get();
return -1;
}
// Read image data into char array
char* buffer = new char[bufferLength];
fileImg.read(buffer, bufferLength);
// Decode data into Mat
cv::Mat matImg;
matImg = cv::imdecode(cv::Mat(1, bufferLength, CV_8UC1, buffer), cv::IMREAD_UNCHANGED);
// Create Window and display it
namedWindow("Image from Char Array", cv::WINDOW_AUTOSIZE);
if (!(matImg.empty()))
{
imshow("Image from Char Array", matImg);
}
cv::waitKey(0);
delete[] buffer;
return 0;
}
Mat has no constructor that takes a char* argument. Try this instead:
std::ifstream file("img.jpg");
std::vector<char> data;
file >> std::noskipws;
std::copy(std::istream_iterator<char>(file), std::istream_iterator<char>(), std::back_inserter(data));
Mat matrixJprg = imdecode(Mat(data), 1);
EDIT:
You should also take a look at LoadImageM.
If you have your data already in a char* buffer one way is to copy the data into an std::vector.
std::vector<char> data(buf, buf + size);