Get mean, stddev and max value from HSV image - c++

I am trying to calculate the Mean, Std and Max Value of hue, saturation and value of an image given in HSV colour space. I split it in three channels to calculate the maximum value of each channel. The problem is I am getting exactly the same value for each channel. The same for mean, std and maximum for hue, saturation and value. I think, maybe I am not understanding well what I get with the functions I am using. Here is my code:
Scalar mean, std;
meanStdDev(image, mean, std, Mat());
vector <Mat> HSV;
split(image, HSV);
double MaxValueH, MaxValueS, MaxValueV;
minMaxLoc(HSV[0], 0, &MaxValueH, 0, 0);
minMaxLoc(HSV[1], 0, &MaxValueS, 0, 0);
minMaxLoc(HSV[2], 0, &MaxValueV, 0, 0);
colour farbe(mean[0], std[0], MaxValueH, mean[1], std [1], MaxValueS, mean[2], std[2], MaxValueV);
return farbe;

Related

Access pixel value of mask using opencv

I got a problem where I need to access pixels of a opencv Mat image container.
I use opencv inRange function to create a mask. In that mask I need to check the value of different pixels, but I won't receive the values I expect to receive.
// convert image to hsv for better color-detection
cv::Mat img_hsv, maskR, maskY, mask1, mask2;
cv::cvtColor(image, img_hsv, cv::COLOR_BGR2HSV);
// Gen lower mask (0-5) and upper mask (175-180) of RED
cv::inRange(img_hsv, cv::Scalar(0, 50, 20), cv::Scalar(5, 255, 255), mask1);
cv::inRange(img_hsv, cv::Scalar(175, 50, 20), cv::Scalar(180, 255, 255), mask2);
// Merge the masks
cv::bitwise_or(mask1, mask2, maskR);
after that I try to read the pixel values where I got extremely high values and even nans, but most of them zeros, which is expected as the mask is only black and white
if (maskR.at<double>(position.x, position.y) == 255)
is there something I'm missing? I tried with double, uchar, int and float
when I print the mask, I can clearly see the 0 and 255 entries(no nans or strange numbers), but when I access them with the at() function, I wont get the same results.
The coordinates of the pixels should be in the range of the Mat as the dimension of the mask is 1080x1920 and non of the coordinates reach over that.
I got the dimension by using cv::size
I finally found the answer to my own question.
It works when I use uchar:
maskR.at<uchar>(position.x, position.y) == 255
I thought this wouldn't work because printing this with std::cout wouldn't give me an output, but the reason for that is that I forgot to cast uchar so it could be printed in the console

Converting CV_32FC1 to CV_16UC1

I am trying to convert a float image that I get from a simulated depth camera to CV_16UC1. The camera publishes the depth in CV_32FC1 format. I tried many ways but the result was not reasonable.
cv::Mat depth_cv(512, 512, CV_32FC1, depth);
cv::Mat depth_converted;
depth_cv.convertTo(depth_converted,CV_16UC1);
The result is a black image. If I use a scale factor, the image will be white.
I also tried to do it this way:
float depthValueF [512*512];
for (int i=0;i<resolution[1];i++){ // go through the rows (y)
for (int j=0;j<resolution[0];j++){ // go through the columns (x)
depthValueOfPixel=depth[i*resolution[0]+j]; // this is location j/i, i.e. x/y
depthValueF[i*resolution[0]+j] = (depthValueOfPixel) * (65535.0f);
}
}
It was not successful either.
Try using cv::normalize instead, which will not only convert the image into the proper data type, but it will properly do the scaling for you under the hood.
Therefore:
cv::Mat depth_cv(512, 512, CV_32FC1, depth);
cv::Mat depth_converted;
cv::normalize(depth_cv, depth_converted, 0, 65535, NORM_MINMAX, CV_16UC1);

Creating Your Own Linear Filter

I am new to Opencv C++. I am trying to convolve a mask with an image. For this I want to create my own mask so that I can use the filter2D array function to convolve my mask with the image. The mask which I want to create is:
char mask [3][3]= {{-1,0,1},{-1,0,1},{-1,0,1}};
For this I have tried the code below(to generate this mask):-
Mat kernel(3,3, CV_8UC1, Scalar(-1,0,1));
i have printed the mask values as
std::cout << kernel;
but the answer which I am getting is
0, 0, 0;
0, 0, 0;
0, 0, 0
I am expecting the answer to be
-1, 0, 1;
-1, 0, 1;
-1, 0, 1
I know I am making a mistake in writing the channels properly. Can anyone help me understand what does the channel(CV_8UC1.... ) means and how to correct it and get the proper output.
CV_8UC1 means 1 channel, 8 bit, uchar image.
Scalar is used to set the value of each channel, not each pixel/coordinate.
Ex 1:
Mat kernel(3,3, CV_8UC1, Scalar::all(0))
would mean creating a 3X3 matrix with 0s and since it is of type CV_8UC1, you can mention only one value, in this case 0.
EX 2:
Mat kernel(3,3, CV_8UC3, Scalar(0,0,255))
means creating a 3X3 matrix with 3 channels since the type is CV_8UC3 and setting channel 1 to 0, channel 2 to 0, channel 3 to 255.
Hence for your purpose to set row values, you cannot use scalar.
Instead do this:
Mat C = (Mat_<double>(3,3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
Check this for more information.
Hope this helps!
You want to create a kernel with negative element for filter2D, then you should't use the data type of CV_8UC1. There is no negative value in unsigned type.
And Mat kernel(3,3, CV_8UC1, Scalar(-1,0,1)); means create a signal- channel-unsigned-char kernel. You set Scalar(-1,0,1) to kernel, then only the first element(that is double -1) is used, while saturate_cast<unchar>(-1) = 0.
Generally, use CV_32FC1 instead.
For example:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(){
float mask[9] = {-1,0,1, -1, 0, 1, -1,0,1};
Mat kernel(3,3, CV_32FC1);
memcpy(kernel.data, mask, sizeof(float)*9);
cout << kernel<<endl;
}
The result:
[-1, 0, 1;
-1, 0, 1;
-1, 0, 1]
A similar question:
How to create cv::Mat from buffer (array of T* data) using a template function?

Accessing elements of cv::Mat OpenCV

I'm trying to apply derivative based color constancy on images and I'm using opencv in c++ to do so.
I'm using Sobel derivatives to calculate my gradients but I don't know what I should cast pixel values of gradient image to in order to be able to read and change them.
Sobel( gradr1, gradr1, gradr1.depth(), 1, 0, 3, 1, 0, BORDER_DEFAULT );
gradr1.at<char>(i,j)
What should I use instead of char?

Watershed boundaries closely surround one area

I am trying to make an average of two blobs in OpenCV. To achieve that I was planning to use watershed algorithm on the image preprocessed in the following way:
cv::Mat common, diff, processed, result;
cv::bitwise_and(blob1, blob2, common); //calc common area of the two blobs
cv::absdiff(blob1, blob2, diff); //calc area where they differ
cv::distanceTransform(diff, processed, CV_DIST_L2, 3); //idea here is that the highest intensity
//will be in the middle of the differing area
cv::normalize(processed, processed, 0, 255, cv::NORM_MINMAX, CV_8U); //convert floats to bytes
cv::Mat watershedMarkers, watershedOutline;
common.convertTo(watershedMarkers, CV_32S, 1. / 255, 1); //change background to label 1, common area to label 2
watershedMarkers.setTo(0, processed); //set 0 (unknown) for area where blobs differ
cv::cvtColor(processed, processed, CV_GRAY2RGB); //watershed wants 3 channels
cv::watershed(processed, watershedMarkers);
cv::rectangle(watershedMarkers, cv::Rect(0, 0, watershedMarkers.cols, watershedMarkers.rows), 1); //remove the outline
//draw the boundary in red (for debugging)
watershedMarkers.convertTo(watershedOutline, CV_16S);
cv::threshold(watershedOutline, watershedOutline, 0, 255, CV_THRESH_BINARY_INV);
watershedOutline.convertTo(watershedOutline, CV_8U);
processed.setTo(cv::Scalar(CV_RGB(255, 0, 0)), watershedOutline);
//convert computed labels back to mask (blob), less relevant but shows my ultimate goal
watershedMarkers.convertTo(watershedMarkers, CV_8U);
cv::threshold(watershedMarkers, watershedMarkers, 1, 0, CV_THRESH_TOZERO_INV);
cv::bitwise_not(watershedMarkers * 255, result);
My problem with the results is that the calculated boundary is (almost) always adjacent to the area common to both blobs. Here are the pictures:
Input markers (black = 0, gray = 1, white = 2)
Watershed input image (distance transform result) with resulting outline drawn in red:
I would expect the boundary to go along the maximum intensity region of the input (that is, along the middle of the differing area). Instead (as you can see) it mostly goes around the area marked as 2, with a bit shifted to touch the background (marked as 1). Do I do something wrong here, or did I misunderstand how watershed works?
Starting from this image:
You can get the correct result simply passing an all-zero image to watershed algorithm. The "basin" is then equally filled of "water" starting from each "side" (then just remember to remove the outer border which is set by default to -1 by watershed algorithm):
Code:
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
Mat1i markers(img.rows, img.cols, int(0));
markers.setTo(1, img == 128);
markers.setTo(2, img == 255);
Mat3b image(markers.rows, markers.cols, Vec3b(0,0,0));
markers.convertTo(markers, CV_32S);
watershed(image, markers);
Mat3b result;
cvtColor(img, result, COLOR_GRAY2BGR);
result.setTo(Scalar(0, 0, 255), markers == -1);
imshow("Result", result);
waitKey();
return(0);
}