I simply can't understand the opencv Mat types:
CV_16UC3 is well known as unsigned 16 bit 3 channel integer. However when I access each channels, I get negative values. According to How should I name my RGB channels, using cv::Mat_ Vec3s is the keyword to access. Here is what I did:
Mat mat_l(img_height,img_width,CV_16UC3);
mat_l = imread("/home/zhao/workspace/rectified_images/l_rectified_fountain.ppm");
cout << vec_mats_l[1].at<Vec3s>(44,500)[0] << " "
<< vec_mats_l[1].at<Vec3s>(44,500)[1] << " "
<< vec_mats_l[1].at<Vec3s>(44,500)[2] << endl;
Output is : 27522 -32382 -32407
why negative values despite type beeing defined unsigned???
try imread(path, -1);
without the flag, the image will be forced into CV_8UC3
(you can check the outcome with cout << mat_l.type();)
also, preallocating mat_l has no effect at all when you use imread, it will be overwritten anyway, so better leave it empty.
"why negative values" - Vec3s is signed. the unsigned version is Vec3w
Related
GIVEN:
The following code fragment:
#include <opencv2/core.hpp>
#include <iostream>
int
main(int argc, char** argv)
{
cv::Mat a = (cv::Mat_<double>(3,1) << 1, 2, 3);
cv::Mat b;
std::cout << "a(before): " << cv::typeToString(a.type()) << std::endl;
std::cout << "b(before): " << cv::typeToString(b.type()) << std::endl;
std::cout << std::endl;
std::cout << "Convert 'a' --> 'b' with type CV_64FC4" << std::endl;
std::cout << std::endl;
a.convertTo(b, CV_64FC4);
std::cout << "a(after): " << cv::typeToString(a.type()) << std::endl;
std::cout << "b(after): " << cv::typeToString(b.type()) << std::endl;
return 0;
}
EXPECTATION:
Should produce, in my understanding, the following output:
a(before): CV_64FC1
b(before): CV_8UC1
Convert 'a' --> 'b' with type CV_64FC4
a(after): CV_64FC1
b(after): CV_64FC4
OUTPUT:
Instead, the output is as follows:
a(before): CV_64FC1
b(before): CV_8UC1
Convert 'a' --> 'b' with type CV_64FC4
a(after): CV_64FC1
b(after): CV_64FC1
QUESTION:
What is going on here? How can I actually convert to the specified target type?
Short answer:
cv::Mat::convertTo does not support changing the number of channels.
Longer answer:
As you can see in the documentation regarding the rtype paremeter of cv::Mat::convertTo (the one you pass CV_64FC4 to):
desired output matrix type or, rather, the depth since the number of
channels are the same as the input has; if rtype is negative, the
output matrix will have the same type as the input.
I.e. convertTo does not handle the case of changing the number of channels (only the bit depth of each channel).
Although it is not documented explicitly, I guess cv::Mat::convertTo extracts the bit depth from rtype and ignores the number of channels.
In your example: a has a single channels, and therefore so is b after the conversion.
In order to see the effect of convertTo, you can pass e.g. CV_32FC1 in order to convert 64 bit doubles to 32 bit floats.
Update:
According to the OP's request (in the comments below), here are examples of changing the number of channels using cv::mixChannels:
cv::Mat img1c = (cv::Mat_<double>(3, 1) << 1, 2, 3);
cv::Mat img4c(img1c.size(), CV_64FC4);
cv::Mat img1c_2(img1c.size(), CV_64FC1);
// Convert 1 channel into 4 (duplicate the channel):
std::vector<int> fromTo1{ 0,0, 0,1, 0,2, 0,3 }; // 0->0, 0->1, 0->2, 0->3
cv::mixChannels(img1c, img4c, fromTo1);
// Convert back to 1 channel (pick one of the channels - the 1st one in this case):
std::vector<int> fromTo2{ 0,0 }; // 0->0 (pick the 1st channel)
cv::mixChannels(img4c, img1c_2, fromTo2);
As I've commented in your other question, if you want to rearrange a Nx1 4-channel Mat to Nx4 1-channel, then you need to use cv::Mat::reshape()
some_mat.reshape(1) converts from Nx1 K-channel (CV_64FC4) to NxK 1-channel (CV_64FC1)
Docs: https://docs.opencv.org/4.x/d3/d63/classcv_1_1Mat.html#a4eb96e3251417fa88b78e2abd6cfd7d8
You really don't need any of that though, because you say you want matrix multiplication... or rather matrix-vector products. you can have that with cv::transform. transform does matrix-vector multiplication, also for lists of vectors (Nx1 K-channel matrices, also NxK 1-ch).
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?)
I've c++ and OpenCV 3.1 and i separated the RGB three channels with these code :
Mat src = imread("original_image.jpg",CV_LOAD_IMAGE_COLOR);
Mat bgr[3] , bluemat ;
split(src,bgr);
bluemat = bgr[0];
std::cout << "bluemat.at<int>(0,1); : " << bluemat.at<int>(0,1) << '\n';
The strange thing is it print out a big number : 1415208581 , why is that ?
Isn't it suppose to be in 0-255 range ? why it is not ?
(expanding comment for search)
A feature of openCV is that the cv::Mat image type is a very simple object based on the original C version of the library.
The cv::Mat contains a field giving the pixel type but the data is stored simply as a block of memory bytes. You can use this block of memory to store and access pixels however you want. It makes it very flexible and easy to link with other libraries, but means you need to manually keep track of what the data is.
So data.at<int>(0,0) will extract the first pixel as if the data was ints (whatever int is on your platform), data.at<uchar> (0,0) will get the first pixel as a byte (which is generally what you want).
The main difference here is casting the memory byte to uchar vs int. What form the data you have depends on how you read it in. CV_LOAD_IMAGE_COLOR read the image as 16-bit/32-bit values. I cannot compile the code you gave me becuase of the memory issues that are created by converting that data to an int.
Now, if you use uchar, that will be solved. The problem however with the cout printing a character, has to do with the overload functions of <<
Mat src = imread("1.jpg", CV_LOAD_IMAGE_COLOR);
Mat bgr[3], bluemat;
split(src, bgr);
bluemat = bgr[0];
std::cout << "bluemat.at<uchar>(0,1); : " << (int)bluemat.at<uchar>(0, 1) << '\n';
What I changed was just to insert the typecast int. This issue is expanded here, but tells the output stream that this is a number, not a character.
This might seem quite weird, but I'd like to embed a PGM image in my source code.
I've tried to create an array of bytes but when I display it, with the very same parameters I find in the original file, the image seems corrupted. Of course, the original is ok.
This is what I've tried:
unsigned char s1_1_pgm[10318] = {
0x50, 0x35, 0x0a, //...
};
cv::Mat n(cv::imread("1.pgm"));
cv::Mat m(cv::Size(92, 112), 16, s1_1_pgm, 276);
std::cout << "CHR " << m.empty() << " type " << m.type() << " step " << m.step << " size " << m.size().width << " x " << m.size().height << std::endl;
std::cout << "PGM " << n.empty() << " type " << n.type() << " step " << n.step << " size " << n.size().width << " x " << n.size().height << std::endl;
imshow("hello", m);
cv::waitKey(0);
The output is obviously this:
CHR 0 type 16 step 276 size 92 x 112
PGM 0 type 16 step 276 size 92 x 112
I am completely new to OpenCV, so I might have done something really stupid.
What is the best way to embed an image in an OpenCV source code?
Thanks!
Well, the problem is that you are using numbers directly to mimic what opencv loaded image. This means that you are putting the step, size and type manually, without knowing what they mean.
OpenCV by default opens an image in the RGB colorspace (but loads it as BGR). This is represented as a matrix of type CV_8UC3 -> C3 is 3 channels and 8U is unsigned char (8 bit size).
276 is correct for this case, where you have 3 channels, but not for a greyscale image. 276 is the step size, how to get this number, easy:
int size = 1;
// use any way to get the size of the type used, either
// manually or maybe sizeof function.
int cols = 92;
int channels = 1;
int step = cols * size * channels;
In the case of a colored image, or at least one in the BGR colorspace, you will need a step of 276 (92 x 1 x 3). Also, the array will be organized in the following way:
B_1 G_1 R_1 B_2 G_2 R_2 .... B_n G_n R_n
This means that each pixel needs 3 spaces of your array (3 uchar values), if you have the data in greyscale, then it will take 3 values to show one pixel, which will look like garbage to the human eye :)
To fix this problem just change
cv::Mat m(cv::Size(92, 112), 16, s1_1_pgm, 276);
to:
cv::Mat m(cv::Size(92, 112), CV_8U, s1_1_pgm, 92);
There is also another problem.... your data is the wrong size... For an image of 92 columns and 112 rows in greyscale, you will need an uchar array of 10304 values, but you are using one of 10318 values. you have some extra values somewhere.... judging by your comment, i think at the beginning.
im trying to access to pixel value of depth map, using kinect, openni and opencv. im using this code
Mat depth;
VideoCapture capture1(CV_CAP_OPENNI);
capture1.grab();
capture1.retrieve(depth,CV_CAP_OPENNI_DEPTH_MAP);
imshow("depth",depth);
waitKey(0);
cout << depth.at<unsigned>(20,20);
system("PAUSE");
the program show me the depth map but when i tried to acccess to the value, produce an error. but if y put:
cout << depth;
then show me all the values.
Since you didn't specified the error, I'll give it a shot: the problem seems to be that you are trying to access elements from another Mat: the one you create is named depth, however the one referenced in the cout call is named depthshow.
According to the documentation for CAP_OPENNI_DEPTH_MAP, your Mat should have 16 bits unsigned integer data per pixel, rather than the 32-bits unsigned int you're trying to use. Therefore, use the following instead:
// uint16_t available in C++11
cout << depth.at<uint16_t>(20,20) << " millimetres";
or
// not 100% sure that all compilers produce 16 bits fields
cout << depth.at<unsigned short int>(20,20) << " millimetres";