Creating Your Own Linear Filter - c++

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?

Related

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);

'compareHist' not working for similar images

I have been trying to find matched image from sample image using histogram matching. for most of the cases my code is working fine. The range of used method, Bhattacharyya, is 0 <= method <= 1.
normally using Bhattacharyya method the output result will close to 0, in case of matched cases. but i have come to a case where both images are almost similar, though there could be some contrast difference.
which is why this procedure is giving higher result...
can anyone help me why this comparison is giving so much bigger value?
src image and test image
int main(){
src_base = imread("images/src.jpg",-1);
src_test1 = imread("images/test.png",-1);
double base_test1 = hsvToHist(src_base, src_test1,3);
cout<< " Bhattacharyya template Base-Test(1) : "<< base_test1<<endl;
return 0;
}
double hsvToHist( Mat src_base, Mat src_test1, int method){
Mat hsv_base, hsv_test1;
cvtColor( src_base, hsv_base, COLOR_BGR2HSV );
cvtColor( src_test1, hsv_test1, COLOR_BGR2HSV );
/// initialization to calculate histograms (Using 50 bins for hue, 60 for saturation)
int h_bins = 50; int s_bins = 60;
int histSize[] = { h_bins, s_bins };
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges };
int channels[] = { 0, 1 };
/// Histograms
Mat hist_base, hist_test1;
/// Calculate the histograms for the HSV images
calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false );
normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() );
calcHist( &hsv_test1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false );
normalize( hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat() );
///'3' for Bhattacharyya
double base_test1 = compareHist( hist_base, hist_test1, method );
return base_test1;
}
The PNG and JPEG images will have different histograms even though they appear the same, because the JPEG is compressed which means information has been removed and the histogram has been essentially filtered and smoothed. Also, the PNG will have a larger range of values than the JPEG. You may get better results with different bin sizes, but it's hard to tell without testing.
The Bhattacharyya distance has an N^2 term in the denominator where N is the number of pixels. In general, this allows similar values for different sizes of images. However, for the icons that you are comparing, the divisor is much smaller. You could scale the metric by a factor related to the image size.
Alternately, you could use the HISTCMP_CORREL method, which produces lower absolute values if the differences between pixels are less significant. This method produces larger values if more pixels are compared.
When you want similar results independent of differences in image size you could compute both metrics and consider the images equal if one of them passes a tight threshold for similarity. Actual thresholds will vary depending on whether you are comparing color or grayscale images, and whether you have pre-processed the images using histogram equalization (see cv::equalizeHist).

OpenCV UMat operators

With cv::Mat one can use ~ for cv::bitwise_not or > to compare 2 matrices.
But cv::UMat doesn't seem to have these operators, understandably you could simply do cv::bitwise_not(umat,umat) (though I've understood copying from a matrix to itself isn't very efficient, correct me if I'm wrong), but how can one compare 2 cv::UMat matrices, or a cv::UMat with a cv::Scalar?
TLDR use OpenCV compare function
You can use .getMat()
cv::UMat A = cv::Mat(1000, 1000, CV_8UC3), B = cv::UMat(1000, 1000, CV_8UC3);
cv::randu(A, Scalar::all(0), Scalar::all(255));
cv::randu(B, Scalar::all(0), Scalar::all(255));
cv::UMat C = A.getMat(cv::ACCESS_READ) > B.getMat(cv::ACCESS_READ);
But this doesn't use cv::UMats' hardware acceleration.
Instead you should just use OpenCV compare function
cv::UMat A = cv::Mat(1000, 1000, CV_8UC3), B = cv::UMat(1000, 1000, CV_8UC3);
cv::randu(A, Scalar::all(0), Scalar::all(255));
cv::randu(B, Scalar::all(0), Scalar::all(255));
cv::UMat C;
cv::compare(A, B, C, CMP_GT);
Not a very efficient answer probably, but from the top of my head...
Compare two cv::UMat you can convert them to a Mat and then use cv::bitwisenot.
Compare cv::UMat with cv::Scalar, you can convert the UMat to Mat and then use this:
Mat_<float> A(3,3); mf << 1,5,5,2,5,5,1,2,3;
// now use a simple MatExpr to get a mask:
Mat mask = (A == 5);
// show results:
cerr << A << endl;
cerr << mask << endl;
------------------------------
[1, 5, 5;
2, 5, 5;
1, 2, 3]
[0, 255, 255;
0, 255, 255;
0, 0, 0]
Depending on whether the Mat is 3,2 or 1 channel, adjust your Scalar.
Hope it helps!!

Get mean, stddev and max value from HSV image

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;

How to use erode and dilate function in opencv?

I'm trying to eliminate the thing around the number with erode and dilate process. I tryed but nothing happened. I changed the values just for see if would change something, but again, nothing has changed. The image continues like in the link above. What about this parameters... I read the documentation but don't quite understand (as you can see, I was guessing in the function). What am I doing wrong?
the image: https://docs.google.com/file/d/0BzUNc6BOkYrNeVhYUk1oQjFSQTQ/edit?usp=sharing
the code:
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
int main ( int argc, char **argv )
{
Mat im_gray;
Mat img_bw;
Mat img_final;
Mat im_rgb = imread("cam.jpg");
cvtColor(im_rgb,im_gray,CV_RGB2GRAY);
adaptiveThreshold(im_gray, img_bw, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV, 105, 1);
dilate(img_bw, img_final, 0, Point(-1, -1), 2, 1, 1);
imwrite("cam_final.jpg", img_final);
return 0;
}
According to official docs, the third argument should be the kernel (or structuring element). You are currently passing 0:
dilate(img_bw, img_final, 0, Point(-1, -1), 2, 1, 1);
Try rewriting it this way:
dilate(img_bw, img_final, Mat(), Point(-1, -1), 2, 1, 1);
In this case, a default 3x3 kernel will be used.
Kernel is basically a matrix. This is multiplied or overlapped on the input matrix(image) to produce the desired output modified(in this case dilated) matrix(image).
Try changing the parameters of Mat() in
dilate(img_bw, img_final, Mat(), Point(-1, -1), 2, 1, 1);
you're basically changing the number of pixels (height and width) of the kernel, which will change the dilation effect on the original pic.
So in the parameters of dilate you use Mat() instead of a number as already stated by esenti.