C++ OpenCV imread with CV_8UC4 halved size - c++

We are trying to read a PNG image of size 400x400 using OpenCV 4.1.0 calling:
*image = imread( filepath, CV_8UC4 );
When I print both cols and rows of the mat calling:
printf("col: %d , rows: %d \n", image->cols, image->rows);
it shows
col: 200, rows: 200
But calling file on the picture it shows:
PNG image data, 400 x 400, 8-bit/color RGBA, non-interlaced.
When I try to put the pic on a second one, it shows that the size is of the picture wrong indeed.
How can I force the reading of the picture giving a 400x400 Mat of Vec4b.

The second argument of imread is flags argument, which expects some color loading flags but not pixel type as you provide. Read possible flags values and their meaning here.
Instead load the image as is, and if it does not contain 4 channels or the type is not what you want, recombine channels (with OpenCV split(), merge(), mixChannels() etc.) and convert it to needed pixel type with cv::Mat::convertTo(). Or use legal imread flags if any of them suits your needs.

Related

image export is returning grey scale image in opencv

I am using Network Optix Video management service. Using their application I am building a plugin. For my purpose I want to export frame as an image from a video. for that I used following code to convert to cv object and saving into my file.
cv::Mat img_color;
cv::Mat img(
videoFrame->height(),/*_rows*/
videoFrame->width(), /*_cols*/
CV_8UC1, //< BGR color space (default for OpenCV) /*_type*/
(void*) videoFrame->data(0), /*_data*/
(size_t) videoFrame->lineSize(0)); /*_step*/
cv::cvtColor(img, img_color, CV_GRAY2RGB);
m_lastVideoFrameTimestampUs = videoFrame->timestampUs();
std::string file_path = "/var/www/html/images/"+std::to_string(m_lastVideoFrameTimestampUs)+".jpg";
cv::imwrite(file_path,img_color);
below screenshot is what I am getting on Network Optix client application.
But, this is what I am getting as an image file on my machine.
cvtColor doesn't have any effect on the image
I think, CV_8UC1 argument should be modified so that I will get RGB image
EDIT - 1:
changes CV_8UC1 to CV_8UC3
Result turned into 3 segments of image
CV_8UC1 means that it is 8-bit single-channel array, you are getting a grayscale image at first and you can not except cvtColor to get it colorized again. cvtColor which you used will convert the image to BGR but all the channels will be in same value so it will continue to seem as grayscale.
In this case you can use CV_8UC3 which means that it is an 8-bit unsigned integer matrix/image with 3 channels(If your image in 3 channels)

Combining pictures with C++ and OpenCV

I'm trying to use C++ and OpenCV to combine pictures, but an error occurs.
My code:
The error:
Assertion failed (size.width>0 && size.height>0) in imshow
What is the 199 in imread(..., 199) supposed to mean?
Valid values are:
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.
a few other options for reduced grayscale images.
Usually you want to load the image in BGR:
Mat img = imread("/path/to/img.png"); // IMREAD_COLOR is default value
or in grayscale
Mat img = imread("/path/to/img.png", IMREAD_GRAYSCALE);

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.

Converting opencv image to gdi bitmap doesn't work depends on image size

I have this code that converts an opencv image to a bitmap:
void processimage(MAT imageData)
{
Gdiplus::Bitmap bitmap(imageData.cols,imageData.rows,stride, PixelFormat24bppRGB,imageData.data);
// do some work with bitmap
}
It is working well when the size of image is 2748 X 3664. But I am tring to process an image wth size 1374 X 1832, it doesn't work.
The error is invalid parameter(2).
I checked and can confirm that:
in 2748 *3664:
cols=2748
rows=3664
stride= 8244
image is continues.
in 1374 X 1832
cols=1374
rows=1832
stride= 4122
image is continues.
So everything seems correct to me, but it generate error.
What is the problem and how can I fix it?
Edit
Based on answer which explained why I can not create bitmap. I finally implemented it in this way:
Mat newImage;
cvtColor(imageData, newImage, CV_BGR2BGRA);
Gdiplus::Bitmap bitmap(newImage.cols,newImage.rows,newImage.step1(), PixelFormat32bppRGB,newImage.data);
So effectively, I convert input image to a 4 byte per pixel and then use the convert it to bitmap.
All credits to Roger Rowland for his answer.
I think the problem is that a BMP format must have a stride that is a multiple of 4.
Your larger image has a stride of 8244, which is valid (8244/4 = 2061) but your smaller image has a stride of 4122, which is not (4122/4 = 1030.5).
As it says on MSDN for the stride parameter (with my emphasis):
Integer that specifies the byte offset between the beginning of one
scan line and the next. This is usually (but not necessarily) the
number of bytes in the pixel format (for example, 2 for 16 bits per
pixel) multiplied by the width of the bitmap. The value passed to this
parameter must be a multiple of four.
Assuming your stride is correct, I think you're only option is to copy it row by row. So, something like:
Great a Gdiplus::Bitmap of the required size and format
Use LockBits to get the bitmap pixel data.
Copy the OpenCV image one row at a time.
Call UnlockBits to release the bitmap data.
You can use my class CGdiPlus that implements all you need to convert from cv::Mat to Gdiplus::Bitmap and vice versa:
OpenCV / Tesseract: How to replace libpng, libtiff etc with GDI+ Bitmap (Load into cv::Mat via GDI+)

pixel type of imread

What is the default pixel type of imread create? I test different images, all of them give me unsigned
char with different channels. Would imread create a pixel type with signed char if I do not ask it
explicitly?
cv::Mat img = cv::imread("lena.jpg", -1); //always give me unsigned char
I checked the document of cv::imread, but it said nothing about the default pixel of imread create.
The link of the document
http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#Mat imread(const string& filename, int flags)
Most .jpg images are 8 bit images. Therfore their default data-type is unsigned char or char.
Some images like .png or .tif also support 16 bit pixel value, so their data type is unsigned short. But it is not necessary as they may be 8 bit.
To load the image as-is in OpenCV, use imread like this:
cv::Mat img = cv::imread(imagePath, CV_LOAD_IMAGE_ANYCOLOR | CV_LOAD_IMAGE_ANYDEPTH);
There are different combinations of these flags:
Load as 8 bit (whatever the original depth is):
CV_LOAD_IMAGE_COLOR
Load with original depth:
CV_LOAD_IMAGE_COLOR | CV_LOAD_IMAGE_ANYDEPTH
Load as grayscale (no matter how many channels the image has):
CV_LOAD_IMAGE_GRAYSCALE
Load as grayscale with original depth:
CV_LOAD_IMAGE_GRAYSCALE| CV_LOAD_IMAGE_ANYDEPTH
In your case, lena.jpg is 8 bit image, so you are getting unsigned char data type.
Update:
For newer versions of OpenCV, use the flags defined by enum in the C++ interface. Just replace CV_LOAD_IMAGE_* with cv::IMREAD_*.
e.g. CV_LOAD_IMAGE_GRAYSCALE becomes cv::IMREAD_GRAYSCALE.