CImg saving images changes values? - c++

So I am using the CImg library to have some fun with images...
The issue is that when I open a file to change its RGB values and save, the modified image does not retain the changed values OR the original value.
CImg<unsigned char> image_out( img.c_str() );
image_out(0,0,0) = 253; //reset new rgb values
image_out(0,0,1) = 248;
image_out(0,0,2) = 251;
image_out.save( "out.jpeg" );
Then when I open the image again, I get this as the new RGB values...
R: 249 G: 249 B: 249
I don't really understand why the values aren't being saved to the ones I set...
edit:
Sorry also mentioning I KNOW the initial value of the pixel (respect to RGB) is 255, 255, 255.

Don't use jpeg, this is a destructive image format that doesn't preserve the values, due to the Dct compression.
Use .png or. Bmp instead.

Related

Save exr/pfm to bitmap CImg

I am trying to convert some bitmap files into custom images (exr, pfm, whatever), and after that, back to bitmap:
CImg<float> image(_T("D:\\Temp\\test.bmp"));
image.normalize(0.0, 1.0);
image.save_exr(_T("D:\\Temp\\test.exr"));
and goes fine (same for .pfm file), I mean the exr file is ok, same for pfm file.
But when this exr, or pfm file I trying to convert back to bitmap:
CImg<float> image;
image.load_exr(_T("D:\\Temp\\test.exr")); // image.load_pfm(_T("D:\\Tempx\\test.pfm"));
image.save_bmp(_T("D:\\Temp\\test2.bmp"));
the result, test2.bmp is black. Complete. Why ? What I am doing wrong ?
Some image formats support saving as float, but most formats save as unsigned 8 bit integer (or uint8), meaning normal image values are from 0 to 255. If you try to save an array that is made up of floats from 0 to 1 into a format that does not support floats, your values will most likely be converted to integers. When you display your image with most image-viewing software, it'll appear entirely black since 0 is black and 1 is almost black.
Most likely when you save your image to bitmap it is trying to convert the values to uint8 but not scaling properly. You can fix this by multiplying normalized values between 0 and 1 by 255. img = int(img*255) or using numpy img = (img*255).astype(np.uint8).
It is also possible that somehow your save function is able to preserve floating point values in the bitmap format. However your image viewing software might not know how to view/display a float image. Perhaps use some imshow function (matplotlib.pyplot can easily display floating point grayscale arrays) between each line of code to check if the arrays are consistent with what you expect them to be.

how to read only one channel of image color from disk in opencv with C++

I need to read large numbers of images with high speed requirements, and just need to handle the Blue channel of a color image.
If I read image with cv::imread(imgName, CV_LOAD_IMAGE_COLOR); It will be very long time, so I want to read only one color image channel. How to do it ??? thanks very much !!
OpenCV doesn't provide any method to load only a specific channel. However, you have a few options.
Load as color image and extract the channel you need
cv::Mat3b img("path/to/image", cv::IMREAD_COLOR);
cv::Mat1b blue;
cv::extractChannel(img, blue, 0);
This is a little faster than using the split approach, but you still need to load the color image.
In a preprocessing stage, load all your images (you can use glob to retrieve all images into a folder), extract the blue channel and store it as grayscale. Then you can load the image as grayscale.
// Preprocessing
cv::String folder = "your_folder_with_images/*.jpg";
std::vector<cv::String> filenames;
cv::glob(folder, filenames);
for (size_t i = 0; i < std::filenames.size(); ++i)
{
cv::Mat3b img = cv::imread(filenames[i], cv::IMREAD_COLOR);
cv::Mat1b blue;
cv::extractChannel(img, blue, 0);
cv::imwrite("some/other/name", blue);
}
// Processing
cv::Mat1b blue = imread("path/to/image", cv::IMREAD_UNCHANGED);
You can improve speed by saving / loading the image in binary format:
// Preprocessing
...
matwrite("some/other/name", blue);
// Processing
cv::Mat1b blue = matread("path/to/image");
I think you can not do this, at least with OpenCV. If you check the documentation of cv::imread you will see that there is no option to read only one color channel:
IMREAD_UNCHANGED: If set, return the loaded image as is (with alpha channel, otherwise it gets cropped).
IMREAD_GRAYSCALE: If set, always convert image to the single channel grayscale image.
IMREAD_COLOR: If set, always convert image to the 3 channel BGR color image.
IMREAD_ANYDEPTH: If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit.
IMREAD_ANYCOLOR: If set, the image is read in any possible color format.
IMREAD_LOAD_GDAL: If set, use the gdal driver for loading the image.
IMREAD_REDUCED_GRAYSCALE_2: If set, always convert image to the single channel grayscale image and the image size reduced 1/2.
IMREAD_REDUCED_COLOR_2: If set, always convert image to the 3 channel BGR color image and the image size reduced 1/2.
IMREAD_REDUCED_GRAYSCALE_4: If set, always convert image to the single channel grayscale image and the image size reduced 1/4.
IMREAD_REDUCED_COLOR_4: If set, always convert image to the 3 channel BGR color image and the image size reduced 1/4.
IMREAD_REDUCED_GRAYSCALE_8: If set, always convert image to the single channel grayscale image and the image size reduced 1/8.
IMREAD_REDUCED_COLOR_8: If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8.
IMREAD_IGNORE_ORIENTATION: If set, do not rotate the image according to EXIF's orientation flag.
If you want, you can split the channels of a matrix after loading it usin Mat::split:
Mat src = imread("img.png",CV_LOAD_IMAGE_COLOR); //load image
Mat bgr[3]; //destination array
split(src,bgr);//split source
//Note: OpenCV uses BGR color order
imwrite("blue.png",bgr[0]); //blue channel
imwrite("green.png",bgr[1]); //green channel
imwrite("red.png",bgr[2]); //red channel

imwrite in opencv gives a black/white image

I wrote a code for watershed segmentation in C API. Now I am converting all those into C++. so, cvsaveimage becomes imwrite. But when I use imwrite ,all i get is a black image.
this is the code:-
Mat img8bit;
Mat img0;
img0 = imread("source.png", 1);
Mat wshed(img0.size(), CV_32S);
wshed.setTo(cv::Scalar::all(0));
////after performing watershed segmentation and
// displaying the watershed image from wshed//
wshed.convertTo(img8bit, CV_32FC3, 255.0);
imwrite("Watershed.png", img8bit);
The original image that I want to save is in wshed. I saw suggestions from the net that we need to convert it to 16 bit or higher so that the imwrite saves it right. Like you see,I tried that. But the wshed image is being displayed correctly when using imshow.The img0 is grey image/black and white while the wshed image is coloured. any help on this?
Edit- I changed the 4th line to
Mat wshed(img0.size(), CV_32FC3);
When calling Mat::convertTo() with a scalar (255 in your case), the values of every matrix item will be multiplied by this scalar value. This will cause all most every result pixel values exceed 255 (i.e. white pixels) except those of 0s where they remain 0 (i.e. black pixels). This is why you will get the black-white pixel in the end.
To make it work, simply change it to:
wshed.convertTo(img8bit, CV_32FC3);
You said:
The original image that I want to save is in wshed. I saw suggestions
from the net that we need to convert it to 16 bit or higher so that
the imwrite saves it right.
If saving the image does not work you should keep in mind that the image data has to be either 8-Bits or 16-Bit unsigned when using the imwrite Function, not 16-Bits or higher.
This is stated in the documentation:
The function imwrite saves the image to the specified file. The image
format is chosen based on the filename extension (see imread() for the
list of extensions). Only 8-bit (or 16-bit unsigned (CV_16U) in case
of PNG, JPEG 2000, and TIFF) single-channel or 3-channel (with ‘BGR’
channel order) images can be saved using this function. If the format,
depth or channel order is different, use Mat::convertTo() , and
cvtColor() to convert it before saving. Or, use the universal
FileStorage I/O functions to save the image to XML or YAML format.

OpenCV imwrite a float image, which conversion to use?

I need to store a float image in OpenCV. Converting it to a CV8U image as suggested by #tomriddle_1234 still stores a black png.
reference.type() = 5
reference.channels() = 1
reference.depth() = 5
How can I convert the image to a 8bit or 16bit so that imwrite can store the image, while maintaining it's float property i.e: the stored image is not "washed out colours" due to conversion/loss of precision!
imshow("5t aligned Mean", reference); //Displays the correct image
//reference.convertTo(reference, CV_8U); //Convert image to 8Bit INCORRECT
reference.convertTo(reference, CV_8U, 255.0, 1/255.0); //Correct image
imwrite(subject.c_str(), reference); //Stores a completely black png
Any suggestions are much appreciated!
You can convert to 16bit by multiplying each float pixel by 2^16-1. Floating point images are stored with values between [0,1] which you want to map to the range [0,2^16-1]
opencv will save 16bit uncompressed in PNG and TIFF with the normal imwrite().
(It will also save them as JPEG although I've had less luck finding things that read 16bit jpeg)
normalize the image before converting between 0 and 255 using CV_NORM_MINMAX

Load Images from memory (libharu) from Magick++ images

I am working on some pdf generation software in c++ based on libharu and I would like to be able to first manipulate images using Magick++ and then load them from memory using libharu function:
HPDF_LoadRawImageFromMem()
Which according to the documentation essentially load images from some void *buffer.
My goal is to be able to get this void* data out of a Magick::Image instance and load this image into my haru pdf based on this data.
I have tried writing to a void*or to a Magick::Blob but the only achievement I have had so far was some black rectangle instead of the image I am expecting.
Does anyone have any experience in converting Raw image data from one library into another one ?
The reason I am trying to do this from memory is because so far I am writing Magick::Image instances into a file and then reading from this file to load then in haru, which is a huge performance hit in the context of my Application.
I'm a little late to answer I guess, but here's a real-life answer.
I successfully added an itk::Image to my pdf using LibHaru so it should work about the same for you. First, you need to know if the library you use is row major or column major. LibHaru (and all the libraries I know) works in row major, so your library should too, or you will need to "transpose" your data.
// Black and white image (8 bits per pixel)
itk::Image<unsigned char, 2>::Pointer image = ...;
const unsigned char *imageData = image->GetBufferPointer();
const HPDF_Image image = HPDF_LoadRawImageFromMem(m_Document,
imageData, width, height, HPDF_CS_DEVICE_GRAY, 8);
// Or color image (24 bits per pixel, 8 bits per color component)
itk::Image<RGBPixel, 2>::Pointer image = ...;
const RGBPixel *imageData = image->GetBufferPointer();
const HPDF_Image image = HPDF_LoadRawImageFromMem(m_Document,
reinterpret_cast<const unsigned char *>(imageData),
width, height, HPDF_CS_DEVICE_RGB, 8);
// Usual LibHaru code. EndText, Position, Draw, StartText, etc.
// This code should not be dependant on the type
InsertImage(image);
I think the only complicated part is the reinterpret_cast. The black and white image don't need one because it's already defined as byte. For example, if you have this image
102 255 255
99 200 0
255 0 100
imageData == {102, 255, 255, 99, 200, 0, 255, 0, 100};
However, if you have this color image
( 0, 0, 255) (0, 255, 255) ( 42, 255, 242)
(200, 200, 255) (0, 199, 199) (190, 190, 190)
imageData == {0, 0, 255, 0, 255, 255, 42, 255, 242, 200, 200, 255, ... }
which LibHaru will inderstand because you tell him to use HPDF_CS_DEVICE_RGB, which means that it will group the data in (R, G, B).
Of course, using ImageMagick, you need to find how to access the first pixel. It's probably a method like data(), begin(), pointer(), etc.
Unfortunately I neither worked with ImageMagic nor libharu, however I have some experience with image processing and since nobody answered yet, maybe I can be of some help.
The problem is probably that there is a plethora of raw image formats and I'm quite sure that both libraries do not have the same understanding of these. What makes things worse is that the raw image interpretation of libharu is virtually not documented. However the conclusions that libharu handles raw data quite straightforward can be drawn from the parameters of: "HPDF_LoadRawImageFromMem".
Width and Height are pretty much self-explanatory, with the only question of the used (probably pixels). More interesting is: "bits_per_component". This parameter probably describes how many bits are used to define one pixel (common values are 8: indexed from a palette of 256 values, 16: indexed from a palette of 65535 values, 24: one byte for red, green, and blue respectivly [RGB], 32: as 24 but with alpha channel or 8 bits for cyan, magenta, yellow, and black [CMYK], 36: as 32 but with 9 bit per value for easier transpostion...). A problem is the lousy documentation of the type: HPDF_ColorSpace, since it probably describes how color values of with: "bits_per_component" are to be interpreted.
A totally different approach seems to be implemented by ImageMagic. An image object seems to have always an image format (JPEG, PNG, GIF), therefore an image object probably never has a "straightforward" memory representation but is encoded.
My recommendation would be to switch the ImagaMagic image to the TIFF format, since it condones compression and therefore has a similar approach to the assumed raw interpretation by libharu.
Hope this helped at least a bit...
Cheers
Mark.
It is never late to answer.
I have used a PNG blob as intermediate step:
Image image;
image.read("file.jpg");
Blob blob;
image.write(blob, "PNG");
HPDF_Image pdfImg = HPDF_LoadPngImageFromMem(doc, (const HPDF_BYTE*)blob.data(), blob.length());
HPDF_Page_DrawImage(doc, pdfImg, 0, 0, image.columns(), image.rows());
PDF document and page creation omitted for brevity.