How to write image in grayscale format in Magick++ - c++

How to save a Magick::Image in a grayscale format? I'm using ImageMagick to decode images and write the result to OpenCV matrix. What I'm doing now is reading the color image and then converting it to grayscale by OpenCV:
Magick::Image image("test.png");
cv::Mat mat(image.rows(), image.columns(), CV_8UC3);
image.write(0, 0, image.columns(), image.rows(), "BGR", Magick::CharPixel, mat.data);
cv::cvtColor(mat, mat, CV_BGR2GRAY);
I'd like to write image to the cv::Mat already in grayscale, without the intermidiate color image. This should be very simple, but wasn't able to find it out from the docs and would appreciate any help.
Also I'd like to know how to detect if an image contains an alpha channel?

To set an image to grayscale, simple call Magick::Image.type( Magick::ImageType ) before writing the image blob to cv.
Magick::Image image("test.png");
image.type( Magick::GrayscaleType );
image.write(0, 0, image.columns(), image.rows(), "BGR", Magick::CharPixel, mat.data);
For detecting if an image has transparent, simply check if Magick::Image.matte() returns true.
Magick::Image image("test.png");
std::cout << "transparent = " << ( image.matte() ? "true" : "false") << std::endl;

Related

Creating transparent PNG image from mask and input image using CPP and OpenCV

I am trying to use the following code for generating a transparent PNG:
my original image is "frame":
cv::Mat mask = cv::Mat::zeros(frame.size(), CV_8U);
cv::drawContours(mask, std::vector<std::vector<cv::Point>>{ maxAreaContour }, -1, cv::Scalar(255), CV_FILLED);
cv::Mat bframe = cv::Mat::zeros(frame.size(),CV_8UC4);
bframe=cv::Scalar(255,255,255,0);
frame.copyTo(bframe, mask);
cv::namedWindow("frg", 0);
cv::imshow("frg", bframe);
cv::namedWindow("mask", 0);
cv::imshow("mask", mask);
And from some reason, I am not able to save the original image with transparent background, i keep get the black background when show as the "bframe" image output
any ideas what I am missing here?
cv::imshow strips out the alpha chanel, so you need to blend image before showing.(i.e. choosing colour or texture to represent background pixels and merge it with your image)
But if you only need to save it, just save it as .png; btw you can set compression params for PNG as:
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(9); //Compression level
try {
imwrite("bframe.png", bframe, compression_params);
}
catch (runtime_error& ex) {
fprintf(stderr, "Exception converting image to PNG format: %s\n", ex.what());
return 1;
}
Ref:
https://docs.opencv.org/3.0-beta/modules/imgcodecs/doc/reading_and_writing_images.html

How to display PGM image using OpenCV

I'm trying to load and display a .PGM image using OpenCV(2.4.0) for C++.
void open(char* location, int flag, int windowFlag)
{
Mat image = imread(location, flag);
namedWindow("Image window", windowFlag);
imshow("Image window", image);
waitKey(0);
}
I'm calling open like this:
open("./img_00245_c1.pgm", IMREAD_UNCHANGED, CV_WINDOW_AUTOSIZE);
The problem is that the image shown when the window is opened is darker than if I'm opening the file with IrfanView.
Also if I'm trying to write this image to another file like this:
Mat imgWrite;
imgWrite = image;
imwrite("newImage.pgm", imgWrite)
I will get a different file content than the original one and IrfanView will display this as my function displays with imshow.
Is there a different flag in imread for .PGM files such that I can get the original file to be displayed and saved ?
EDIT: Image pgm file
EDIT 2 : Remarked that: IrfanView normalizes the image to a maximum pixel value of 255 .
In order to see the image clearly using OpenCV I should normalize the image also when loading in Mat. Is this possible directly with OpenCV functions without iterating through pixels and modifying their values ?
The problem is not in the way data are loaded, but in the way they are displayed.
Your image is a CV_16UC1, and both imshow and imwrite normalize the values from original range [0, 65535] to the range [0, 255] to fit the range of the type CV_8U.
Since your PGM image has max_value of 4096:
P2
1176 640 // width height
4096 // max_value
it should be normalized from range [0, 4096] instead of [0, 65535].
You can do this with:
Mat img = imread("path_to_image", IMREAD_UNCHANGED);
img.convertTo(img, CV_8U, 255.0 / 4096.0);
imshow("Image", img);
waitKey();
Please note that the values range in your image doesn't correspond to [0, 4096], but:
double minv, maxv;
minMaxLoc(img, &minv, &maxv);
// minv = 198
// maxv = 2414
So the straightforward normalization in [0,255] like:
normalize(img, img, 0, 255, NORM_MINMAX);
img.convertTo(img, CV_8U);
won't work, as it will produce an image brighter than it should be.
This means that to properly show your image you need to know the max_value (here 4096). If it changes every time, you can retrieve it parsing the .pgm file.
Again, it's just a problem with visualization. Data are correct.

Getting masked area to be transparent?

So far i have managed to use masks and get the second image from the first. But what i want is the black area in second image to be transparent (i.e the output i an trying to get is the third image) Here is the code so far. Please advice me on this.
EDIT: Third one is from photoshop
//imwrite parameters
compression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
compression_params.push_back(100);
//reading image to be masked
image = imread(main_img, -1);
//CV_LOAD_IMAGE_COLOR
namedWindow("output", WINDOW_NORMAL);
//imshow("output", image);
//Creating mask image with same size as original image
Mat mask(image.rows, image.cols, CV_8UC1, Scalar(0));
// Create Polygon from vertices
ROI_Vertices.push_back(Point2f(float(3112),float(58)));
ROI_Vertices.push_back(Point2f(float(3515),float(58)));
ROI_Vertices.push_back(Point2f(float(3515),float(1332)));
ROI_Vertices.push_back(Point2f(float(3112),float(958)));
approxPolyDP(ROI_Vertices, ROI_Poly, 1, true);
// Fill polygon white
fillConvexPoly(mask, &ROI_Poly[0] , ROI_Poly.size(), 255, 8, 0);
//imshow("output", mask);
// Create new image for result storage
imageDest = cvCreateMat(image.rows, image.cols, CV_8UC4);
// Cut out ROI and store it in imageDest
image.copyTo(imageDest, mask);
imwrite("masked.jpeg", imageDest, compression_params);
imshow("output", imageDest);
cvWaitKey(0);
This can be done by first setting its alpha value to 0 of the regions that you want to make them fully transparent (255 for others), and then save it to PNG.
To set the alpha value of pixel-(x,y), it can be done:
image.at<cv::Vec4b>(y, x)[3] = 0;
PS: you need to convert it to 4-channel format first if the image is not currently. For example:
cv::cvtColor(image, image, CV_BGR2BGRA);
Updated: It will be easier if you have already computed the mask for the ROI region, where you can simply merge it with the original image (assume having 3 channels) to get the final result. Like:
cv::Mat mask; // 0 for transparent regions, 255 otherwise (serve as the alpha channel)
std::vector<cv::Mat> channels;
cv::split(image, channels);
channels.push_back(mask);
cv::Mat result;
cv::merge(channels, result);

Open CV image saving in smaller size without compressions

The question i have is the following:
I have a camera( with resolution Resolution : 640 x 480 px) and I get an image from that camera (I get an 8 bit/ pixel grayscale image) after the image acquisition I save the image in a bmp format. My code is the followig :
Mat img2(640,480,CV_8UC1,0);
cap.read(img2);
bool succes = imwrite("D:\\TestImage3.bmp",img2);
if(!succes){
cout << "Failed to save the image";
return -1;
}
namedWindow("myWindow",CV_WINDOW_AUTOSIZE);
imshow("myWindow",img2);
The saved image is very large almost 1 MB and i want a smaller image without losing any information (without compresing the image)???
The second question on this topic is:
even if the image is gray some times I still get some rgb noise, its like I would have set a 3 channel setting instead of 1 channel setting for my image
If anyone knows the answer please let me know, I would be very grateful
Thanks for your time!
You can save your image as PNG which is an lossless image compression format.
bool succes = imwrite("D:\\TestImage3.png",img2);
With the cv::imwrite function you can pass additional parameters depending on the image format.
PNG is a lossless image format but you can still chose the level of compression for example :
Mat img2;
cap.read(img2);
cvtColor(img2, img2, CV_BGR2GRAY); // Convert to single channel
vector<int> compression_params;
compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
compression_params.push_back(9);
bool succes = imwrite("D:\\TestImage3.bmp", img2, compression_params);
if(!succes)
{
cout << "Failed to save the image"; return -1;
}
imshow("myWindow",img2);
waitKey(0);
Just use the default constructor for Mat with no params.
Mat img2;
cap.read(img2);
cvtColor(img2, img2, CV_BGR2GRAY); // Convert to single channel
bool succes = imwrite("D:\\TestImage3.bmp", img2);
if(!succes)
{
cout << "Failed to save the image"; return -1;
}
imshow("myWindow",img2);
waitKey(0);
Also, bmp is known for its large uncompressed size. Use .png instead.

Canny edge detection - grayscale images always coming up as 3-channel, unusable?

I am working through the book "Learning OpenCV" from the O'Reilly series and am trying to perform a canny edge detection sample.
Any grayscale image I choose seems to come up as having 3 channels, and to the best of my knowledge, canny only works with single channel images, so this always fails. I am even using the images provided by OpenCV.
Here is my code..
IplImage* doCanny(IplImage* in, double lowThresh, double highThresh, double aperture)
{
if(in->nChannels != 1)
return(0); //canny only handles gray scale images
IplImage* out = cvCreateImage(cvSize(in->width, in->height), IPL_DEPTH_8U, 1);
cvCanny(in, out, lowThresh, highThresh, aperture);
return(out);
};
IplImage* img = cvLoadImage("someGrayscaleImage.jpg");
IplImage* out = doCanny(img, 10, 100, 3);
Why might this always give me 3-channel images? How can I solve this?
You can use this method with another parameter
IplImage* cvLoadImage(const char* filename, int iscolor=CV_LOAD_IMAGE_COLOR)
#define CV_LOAD_IMAGE_COLOR 1
#define CV_LOAD_IMAGE_GRAYSCALE 0
#define CV_LOAD_IMAGE_UNCHANGED -1
The default parameter is load image with color. What you have to do is to load it with grayscale
Here is an example
cvLoadImage("yourimage.jpg", CV_LOAD_IMAGE_GRAYSCALE);
Here is the detail explanation for that method. You can look at here for more details:
Open CV 2.0 References
scolor – Specific color type of the loaded image: if $ > 0 $, the loaded image is forced to be a 3-channel color image; if 0, the loaded image is forced to be grayscale; if $ < 0 $, the loaded image will be loaded as is (note that in the current implementation the alpha channel, if any, is stripped from the output image, e.g. 4-channel RGBA image will be loaded as RGB).