Save Image into a file in C++ - c++

I want to save an image into a custom file, in order to secure all images of my program.
I tried to save every pixels (as Uint32) of an image like this (I am using SFML)
void fromPNGtoCustomFile(sf::Texture texture, std::string path)
{
std::ofstream fo;
fo.open(path);
sf::Image image=texture.copyToImage(); //GET IMAGE OF THE TEXTURE
fo << image.getSize().x << " " << image.getSize().y << " "; // WRITE THE SIZE OF THE IMAGE
for(unsigned int i=0; i< image.getSize().x; i++)
{
for(unsigned int j=0; j< image.getSize().y; j++)
{
fo << image.getPixel(i, j).toInteger() << " ";
// image.getPixel(x, y).toInteger() RETURNS A Uint32 VALUE
}
}
fo.close();
}
Then I load image from the file using the same logic.
It worked, but I realised that the size of the file I created was around 250 Mb, when my original .png image was only 8 Mb. If I compress the new file using 7Zip, I get a 8 Mb file, as the original size. I do not understand why I get this size.
I don't really know what is the best way to create custom file for saving images.
Do you have any suggestions, or correction of my code?

To answer the question of why you are getting such a big file size, it's because you are writing the information for every single pixel independently, so your file ends up as large as it can be. In order to reduce the size, the compressed formats take advantage of, for instance, the fact that there is usually a large number of neighbouring pixels that are the same colour. For example, an image of the sky will very likely contain an immense amount of pixels of the same shade of blue. So what you need to do is to define an algorithm with some sort of special code to denote that "the next N pixels are some RGB colour" or something to that effect.
A good source on data compression for images is on this chapter of the DSP Guide: http://www.dspguide.com/ch27.htm

Related

How to mitigate data loss when converting and manipulating in LAB opponent color space?

I am very new to openCV and learning as I go.
I am using openCV 4.3, however the following is from 2.4:
"If you use cvtColor with 8-bit images, the conversion will have some information lost. For many applications, this will not be noticeable but it is recommended to use 32-bit images in applications that need the full range of colors or that convert an image before an operation and then convert back."
I am using a 24bit jpg image and applying some minor color correction to the LAB channels before converting back to BGR (similar to the warning in the 2.4 notes).
I load the image with:
//Ask user for filename and load with IMREAD_COLOR
string filename, finalFilename;
cout << "Which would you like to load? " << "\n";
cin >> filename;
cout << "What would you like to call the final image? " << "\n";
cin >> finalFilename;
Mat img = imread(filename, IMREAD_COLOR);
//Convert to CIEL*a*b* format and split for histogram
Mat imgLab;
cvtColor(img, imgLab, COLOR_BGR2Lab);
//Checking type and depth of image to ensure CV_8U (Note: This may have to be converted as to not lose information)
cout << "IMREAD_COLOR Loaded this image with a depth value of " << img.depth() << " and a type value of " << img.type() << "\n";
cout << "cvtColor has changed this image to one with a type value of " << imgLab.type() << "\n\n";
Then I manipulate the channels later on after assigning them to temp variables:
{
for (int j = 0; j < img.cols; j++)
{
modA.at<uint8_t>(i, j) = (float)tempA.at<uint8_t>(i, j) + (0.7f * ((float)mask.at<uint8_t>(i, j))/255 * (128-((float)aBlur.at<uint8_t>(i, j))));
modB.at<uint8_t>(i, j) = (float)tempB.at<uint8_t>(i, j) + (0.7f * ((float)mask.at<uint8_t>(i, j))/255 * (128-((float)bBlur.at<uint8_t>(i, j))));
}
}
Mask is a 1 channel 8 bit matrix that holds values from 0-255.
aBlur is tempA with a Gaussian blur applied (same applies to tempB/bBLur).
For some reason, after the conversion, the channels seem to still be skewed from 0-255. (though I could be wrong about this, I noticed that they went above 127 and never below about 100, a bit strange.
I have done a few tests and the type before converting to LAB and after remain the same (CV_8UC3). There is a warning due to the (float) code that there could be information loss:
Severity Code Description Project File Line Suppression State
Warning C4244 '=': conversion from 'float' to '_Tp', possible loss of data OpenCvLearning
My question is:
Am I losing information by this process? I noticed my output was not as pleasant as the paper I am attempting to reimplement.
Here is the original, my imp, and their result:
Colours coming out more gray than they should
UPDATE
So I have updated my code to work with float, which now has many more points possible data (2^32). However when polling the data it is still in 8bit (0-255).
I am attempting to use normalize min n max with the old min and max of the 8bit function and scaling to 0-1 for 32 bit. However, I am concerned about scaling back to 8bit without introducing 8bit error (how can I normalize 0-255 in a matrix that doesn't have 0 or 1 in the 32 bit version?)

streaming a bitmap while it is modified in C++

What I am trying to do is use some process to alter the contents of a bitmap image ( dimensions and everything else is kept constant). While I run a process to perform transformations on it, I want to concurrently stream the image to my screen as it happens. So it's basically video without a series of images, but rather the same image in different stages of a transformation.
I am trying to do this because the bitmap I want to do this with is large, so actually saving each state as its own image and then combining will not be possible due to memory constraints.
My question is regarding the "live image display" while another process modifies the image, is there any way something like this can be done with a bitmap image?
You could stream the images in a video encoder directly.
For example you can feed ffmpeg with raw PGM/PPM images as input and you will get the compressed video as output without the need to ever create the actual images on disk.
Writing a PGM or PPM image means just generating a few header bytes followed by actual pixel values and thus is trivial to do with any language without the need of any image library. For example:
#include <stdio.h>
#include <math.h>
#include <algorithm>
int main(int argc, const char *argv[]) {
int w = 1920, h = 1080;
for (int frame=0; frame<100; frame++) {
// output frame header
printf("P5\n%i %i 255\n", w, h);
for (int y=0; y<h; y++) {
for (int x=0; x<w; x++) {
double dx = (x - w/2),
dy = (y - h/2),
d = sqrt(dx*dx + dy*dy),
a = atan2(dy, dx) - frame*2*3.14159265359/100,
value = 127 + 127*sin(a+d/10);
// output pixel
putchar(std::max(0, std::min(255, int(value))));
}
}
}
return 0;
}
generates on standard output a sequence of images that can be combined in a video directly. Running the program with
./video | ffmpeg -i - -r 60 -format pgm -y out.mp4
will generate a video named out.mp4. The video will be at 60fps (what -r is for) created from source images in PGM format (-format pgm) from standard input (option -i -) overwriting the output file if it's already present (-y).
This code was tested on linux; for this approach to work on windows you need also to set stdout to binary more with _setmode(fileno(stdout), _O_BINARY); or something similar (I didn't test on windows).
A more sophisticated way to do basically the same would be to start a child process instead of using piping thus leaving standard output usable by the program.
If you're just trying to see the results real-time, you can use something similar to page flipping / double buffering.
Basically: create two images. Image 1 is a "drawing buffer" while Image 2 is a "display buffer." Perform some portion of your drawing to image 1. Then, copy its complete contents to image 2. Continue drawing to image 1, copy, draw, copy, draw... etc. Insert a delay before each copy.

Unable to access pixel values from greyscale image

I am reading an image of size 1600x1200 as greyscale and then trying to access pixel value at location (1201,0). I get segfault in the end as shown in comments:
Mat gray_image = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE); // Read the file
if(! gray_image.data ) // Check for invalid input
{
cout << "Could not open or find the image" << std::endl ;
return -1;
}
const int width = gray_image.cols;
const int height = gray_image.rows;
cout<<gray_image.cols<<endl; // 1600
cout<<gray_image.rows<<endl; // 1200
cout<<(int)gray_image.at<uchar>(1201,0)<<endl; //SEGFAULT
You already stated that you are reading an image of size 1600x1200, then how can you access 1201 element from a total of 1200 rows only, actually you have misunderstood the convention of mat.at<>(). I would recommend you to use mat.at<>(cv::Point(p_x, p_y)) in this way you would never get confused with rows and cols to be passed in the mat.at<>().
EDIT : However, creating a new cv::Point is not a recommended way of accessing pixels as suggested by #Micka, So consider it as a workaround and don't rely completely on cv::Point() to access a pixel. The best possible way to iterate the pixels is defined in Opencv documentation.
It should be .at(row,col) not (col,row) and you dont have a 1201th row thats why its a seg fault

save image buffer as bitmap image

I have a image buffer stored as a linear array[640*480] of unsigned integer type, and I want to save this array as a bitmap image which can be viewed. I have captured an image from my camera and retrieved its image buffer from a GigE cable using in c++ code. So please tell me how to write an integer array of RGB values to Bitmap in C++ along with the header files required. I have stream buffer as
if (Result.Succeeded())
{
// Grabbing was successful, process image
cout << "Image #" << n << " acquired!" << endl;
cout << "Size: " << Result.GetSizeX() << " x "
<< Result.GetSizeY() << endl;
// Get the pointer to the image buffer
const unsigned int *pImageBuffer = (int *) Result.Buffer();
the pImagebuffer is the image Buffer and please ignore the Functions as they belong to a custom compiler. I just want to convert the RGB values to bitmap image and then save it
also the pImageBuffer is giving me the R=G=B as photo is mono chrome.
Save the pixel data together with a simple BMP-file header, appropriately initialized. See the format description here.

Fast accessing pixel values of jpeg images

CompVision once again, I'm working with jpeg images in my application. Just because I'm a bit familiar with MFC and ATL, I used CImage to access pixel values.
For my needs I calculate brightness matrix for the image during initialization. Function goes like this (Image is the name of my own class, unimportant, bright is float[][]):
void Image::fillBrightnessMatrix(){
COLORREF val;
for(int i=0;i<width;i++){
for(int j=0; j<height;j++){
val=src.GetPixel(i,j);
bright[i][j]=rgb_to_L(val);
}
}
}
Where src is an instance of CImage class, rgb_to_L - some function that calculates brightness of the color.
Examining the performance of my app, I discovered that GetPixel is the most expensive operation, and it significantly (really, ~700 times slower than any other operation) slows down the whole initializing of image. The question is, which library can you suggest for fast access to single pixel values? I don't need any other operations but loading jpeg image and accessing single pixels. Performance is important, because my application works with set of ~3000 images and I can't wait for hours to get results.
Use CBitmap::GetBits() to get a raw pointer to the pixel data. You can now directly party on the pixels without going through the expensive GetPixel() method. There are a number of things you need to be careful with when you do this:
You have to use CBitmap::GetPitch() to calculate the offset to the start of a line. The pitch is not the same as the width.
Lines in the bitmap are stored upside-down
You have to deal with the pixel format yourself. A 24bpp image stores 3 bytes per pixel. An indexed format like 8bpp requires looking up the color in the color table. 32bpp is the easy one, 4 bytes per pixel and the pitch is always the same as the width.
I always recommend OpenCV.
This is a humble code snippet to get you started:
IplImage* pRGBImg = cvLoadImage("c:\\test.jpg", CV_LOAD_IMAGE_UNCHANGED);
if (!pRGBImg)
{
std::cout << "!!! cvLoadImage failed !!!" << std::endl;
exit(1);
}
int width = pRGBImg->width;
int height = pRGBImg->height;
int bpp = pRGBImg->nChannels;
for (int i=0; i < width*height*bpp; i+=bpp)
{
if (!(i % (width*bpp))) // print empty line for better readability
std::cout << std::endl;
std::cout << std::dec << "R:" << (int) pRGBImg->imageData[i] <<
" G:" << (int) pRGBImg->imageData[i+1] <<
" B:" << (int) pRGBImg->imageData[i+2] << " ";
}
You should probably extract the jpeg to raw data, then access the raw data instead of GetPixel.