Im using OpenCV and I have a Mat object of size 1024*1024(extracted from a photo and manipulated) and the values are in the range [1..25].for example:
Mat g;
g=[1,5,2,14,13,5,22,24,5,13....;
21,12,...;
..
.];
I want to represent these values as an image.It is only an illustration image to show the different areas,each area with a color.
For example: all the values that equals 1=red, all the values that equals 14=blue, and so on..
and then construct and display this photo.
Anybody have an idea how should i proceed?
Thanks!
If you are not too fussed what colors you get, you can scale your data (so it almost fills the 0 to 255 range) then use an inbuilt colormap.
e.g.
cv::Mat g = ...
cv::Mat image;
cv::applyColorMap(g * 10, image, COLORMAP_RAINBOW);
See the applyColorMap() doco
there are colormaps , but they won't help if your data is in the [0..25] range only. so you probably ned to roll your own version of that:
Vec3b lut[26] = {
Vec3b(0,0,255),
Vec3b(13,255,11),
Vec3b(255,22,1),
// all the way down, you get the picture, no ?
};
Mat color(w,h,CV_8UC3);
for ( int y=0; y<h; y++ ) {
for ( int x=0; x<w; x++ ) {
color.at<Vec3b>(y,x) = lut[ g.at<uchar>(y,x) ];
// check the type of "g" please, i assumed CV_8UC1 here.
// if it's CV_32S, use g.at<int> , i.e, you need the right type here
}
}
Related
I have a Mat Dist (CV_8U) done by distanceTransform.
Now I have to check each coordinate of Dist is > 0 and modify the value of another Mat M = Mat :: zeros
the code is
int main(){
....
for(i=0;i<Dist.rows;++i)
{
for(j=0;j<Dist.cols;++j)
{
if(Dist.at<uchar>(i,j) > 0){
M.at<uchar>(i,j)=2;
}
}
}
....
}
but I error cv :: exception.
I looked in the documentation and elsewhere , I tried to change from uchar to vec3b . I modified the exception in visual studio 2015 but nothing .
Where am I wrong?
The function distanceTransform does not return ad CV_8U, it is CV_32 as we can see in the documentation:
dst – Output image with calculated distances. It is a 32-bit
floating-point, single-channel image of the same size as src .
So the code should not read uchar, but float
...
if(Dist.at<float>(i,j) > 0.f)
...
In case you used the labels from distance transform, in the documentation we have the following:
labels – Optional output 2D array of labels (the discrete Voronoi
diagram). It has the type CV_32SC1 and the same size as src . See the
details below.
So, in this case you should access it as an int
...
if(Dist.at<int>(i,j) > 0)
...
I hope this helps you.
By the way, maybe an epsilon value instead of the 0 will be better....
I am trying to convert an image to double precision using opencv. I am trying to imitate the im2double function available in MATLAB in c++. So, for this what i did was..
Mat IMG = imread("lena.bmp");
Size size(8,8);
Mat img,img_re,grey;
cvtColor( IMG, img, CV_BGR2GRAY );
resize(img,img_re,size);
img_re.convertTo( grey, CV_64FC3, 1.0/255.0 );
std::cout<<grey<<std::endl;
unsigned char *input = (unsigned char*)(grey.data);
grey: [0.3764705882352941, 0.5176470588235293, 0.4352941176470588, 0.8274509803921568;
0.392156862745098, 0.5254901960784314, 0.7372549019607844, 0.6431372549019607;
0.4431372549019608, 0.6431372549019607, 0.7176470588235294, 0.5607843137254902;
0.5333333333333333, 0.3254901960784314, 0.6862745098039216, 0.8431372549019608]
The data stored in grey is almost similar to the data obtained from matlab. the pixels have a range of [0,1]here. But ,my problem starts here. I want to now access the pixel values from 'grey' and save it to a boost matrix.
So for this i use..
for (unsigned i=0; i < height; ++i)
{
for (unsigned j=0; j < width; ++j )
{
image(i,j) = input[grey.step * j + i ];
}
}
image:: [4,4]((24,24,144,144),(24,24,144,144),(24,216,144,224),(24,63,144,63))
After this step all the values in the matrix have a range of [0,255]. grey scale images are between [0,255] but why do it get the values between [0,1] in the first case.
please stay away from accessing Mat's raw 'data' pointer, and use:
grey.at<double>(i,j);
instead.
also, if im_re is a 1 channel, grayscale image, your typeflag is wrong, should be:
img_re.convertTo( grey, CV_64F, 1.0/255.0 );
I have used Canny edge detector to successfully identify the edges of a given image. I'm struggling with finding specific points on this detected edge line.
My approach:
I used the cv::canny function in opencv and the output is stored in cv::Mat format. I want to iterate through the all values of the matrix and identify all those pixels where the edge is present so that I can detect the specific points on the detected edge line.
Function used:
cv::Canny(frame_gray,contours,50,150);
The output is stored in contours and it is of type CV_8UC3
To access the pixel value, have tried
contours.at<int>(i,j) != 0
and also
contours.at<uchar>(i,j) != 0
Will greatly appreciate help in the above. If the approach is correct and am missing something or else if i should try another approach
Thanks
Edit:
for(int i=0;i<img_width;i++)
{
if((int)contours.at<uchar>(i,neckcenter.y) > 0 )
{
Point multipoints(i,neckcenter.y);
circle( contours, multipoints, neckpoint, Scalar( 255, 0, 0 ),4, 8, 0 );
cout << (int)contours.at<uchar>(i,neckcenter.y) << endl;
}
}
I am using the above code which forms a small circle of radius 1 (defined by neckpoint) where it detects a point on and edge. The neckcenter.y is a constant value derived from an earlier calculation. What am i doing wrong here ?
Output of the code -
you probably want a grayscale pass before applying Canny:
Mat gray;
cvtColor(bgr,gray,CV_BGR2GRAY); // now gray is a 8bit, uchar Mat
Mat contours;
cv::Canny(gray,contours,50,150);
// now you're safe to use:
uchar value = contours.at<uchar>(i,j);
The syntax:
contours.at<uchar>(i,j)
Is correct for your case in terms of data type (i.e. a grayscale image). The problem is possibly hinted at by this line:
for(int i=0;i<img_width;i++)
When you access OpenCV pixels using at, you must specify the pixel position as (row, col), so your indexing is the wrong way round. Try this in all places where you access pixels:
contours.at<uchar>(j,i)
From the OpenCV documentation:
You have a 3 channel image of the type unsigned char. To access it you should use the cv::Vec3b type. Here is how to do it:
int channel = 0;//or 1 or 2
contours.at<cv::Vec3b>(i,j)[channel]
To check if all elements are 0:
contours.at<cv::Vec3b>(i,j)[0]==0 && contours.at<cv::Vec3b>(i,j)[1]==0 && contours.at<cv::Vec3b>(i,j)[2]==0
But where do you have the information that the image type of contours is CV_8UC3 ?
I am using Ubuntu 12.04 and OpenCV 2
I have written the following code :
IplImage* img =0;
img = cvLoadImage("nature.jpg");
if(img != 0)
{
Mat Img_mat(img);
std::vector<Mat> RGB;
split(Img_mat, RGB);
int data = (RGB[0]).at<int>(i,j)); /*Where i, j are inside the bounds of the matrix size .. i have checked this*/
}
The problem is I am getting negative values and very large values in the data variable. I think I have made some mistake somewhere. Can you please point it out.
I have been reading the documentation (I have not finished it fully.. it is quite large. ) But from what I have read, this should work. But it isnt. What is going wrong here?
Img_mat is a 3 channeled image. Each channel consists of pixel values uchar in data type.
So with split(Img_mat, BGR) the Img_mat is split into 3 planes of blue, green and red which are collectively stored in a vector BGR. So BGR[0] is the first (blue) plane with uchar data type pixels...hence it will be
int dataB = (int)BGR[0].at<uchar>(i,j);
int dataG = (int)BGR[1].at<uchar>(i,j);
so on...
You have to specify the correct type for cv::Mat::at(i,j). You are accessing the pixel as int, while it should be a vector of uchar. Your code should look something like this:
IplImage* img = 0;
img = cvLoadImage("nature.jpg");
if(img != 0)
{
Mat Img_mat(img);
std::vector<Mat> BGR;
split(Img_mat, BGR);
Vec3b data = BGR[0].at<Vec3b>(i,j);
// data[0] -> blue
// data[1] -> green
// data[2] -> red
}
Why are you loading an IplImage first? You are mixing the C and C++ interfaces.
Loading a cv::Mat with imread directly would be more straight-forward.
This way you can also specify the type and use the according type in your at call.
I am trying to pass in a HSV frame from a video to the function, but the function does not seem to do anything to it. What am I doing wrong? The function is supposed to go through each pixel, and depending on its hue range supposed to make it black or white, leaving me with a binary image. Instead it doesn't seem to affect the HSV image at all....
Thanks
PS sorry for the bad code formatting, StackOverflow isn't allowing me to post the original format.
void sort (IplImage *skinmask)
{
for (int row=0; row<=skinmask->height;row++)
{
uchar* pixelrow=(uchar*)(skinmask->imageData+(row*(skinmask->widthStep)));
for (int column=0; column<=skinmask->width; column++)
{
if (6<pixelrow[3*column]<36)
{
pixelrow[3*column]=256;
pixelrow[(3*column)+1]=256;
pixelrow[(3*column)+2]=256;
}
else
{
pixelrow[3*column]=0;
pixelrow[(3*column)+1]=0;
pixelrow[(3*column)+2]=0;
}
column++;
}
row++;
}
cvMorphologyEx(skinmask,skinmask,NULL,NULL,CV_MOP_CLOSE,1);
}
Doing an operation like thresholding pixel-by-pixel is usually the wrong way to go about achieving this in OpenCV - there are functions that work on whole image arrays that are simpler and are already optimized for speed.
In this case try first splitting the image to separate out the H/S/V channels, then threshold on the Hue channel to get a mask (you may have to use the intersection of two masks, which you can do using a multiply or "bitwise and") - the resulting mask is your black and white image.
(I realise I've linked to the C++ documentation, but I'm sure you can find the equivalent functions in the old-style OpenCV docs)
Update
Ok, I'll try to write some code to show what I mean. I also found the function I was looking for, which is better than Threshold, it is InRangeS. This lets you put upper and lower bounds on all the channels at once, and it applies them all into your mask for you.
void HSVImageToMask(IplImage * image, cvMat * mask)
/* mask should be the same size as image, and of type CV_8UC1 */
/* e.g. cvMat * mask = cvCreateMat(image->width, image->height, CV8UC1); */
{
double hMin = 6;
double hMax = 36;
double sMin = 10; /* not sure what value you need */
double sMax = 245; /* not sure what value you need */
double vMin = 0;
double vMax = 255;
CvScalar hsvMin = cvScalar(hMin, sMin, vMin);
CvScalar hsvMax = cvScalar(hMax, sMax, vMax);
cvInRangeS(image, hsvMin, hsvMax, mask);
}
PS. I figured out the problem with your original code - you should be using 255 instead of 256 as your "white" value. This method is still better though :)
PPS. We didn't need them after all but for future reference:
"bitwise and":
cvAnd(const CvArr* src1, const CvArr* src2, CvArr* dst)
If you have two black and white masks, this will give you the intersection. Use cvOr to get the Union.