How to implement convolution Gaussian blur without padding using OpenCV in C++ - c++

I am implementing convolution Gaussian blur with OpenCV without using OpenCV functions, but my code is programmed with padding.
Now I want to remove the padding from the matrix so that my code can be executed without the matrix with padding. How can I modify the code below to do so?
class Filteration {
private:
// member function to pad the image before convolution
Mat padding(Mat img, int k_width, int k_height, string type)
{
Mat scr;
img.convertTo(scr, CV_64FC1);
int pad_rows, pad_cols;
pad_rows = (k_height - 1) / 2;
pad_cols = (k_width - 1) / 2;
Mat pad_image(Size(scr.cols + 2 * pad_cols, scr.rows + 2 * pad_rows), CV_64FC1,
Scalar(0));
scr.copyTo(pad_image(Rect(pad_cols, pad_rows, scr.cols, scr.rows)));
// mirror padding
if (type == "mirror")
{
for (int i = 0; i < pad_rows; i++)
{
scr(Rect(0, pad_rows - i, scr.cols, 1)).copyTo(pad_image(Rect(pad_cols,
i, scr.cols, 1)));
scr(Rect(0, (scr.rows - 1) - pad_rows + i, scr.cols,
1)).copyTo(pad_image(Rect(pad_cols,
(pad_image.rows - 1) - i, scr.cols, 1)));
}
for (int j = 0; j < pad_cols; j++)
{
pad_image(Rect(2 * pad_cols - j, 0, 1,
pad_image.rows)).copyTo(pad_image(Rect(j, 0, 1, pad_image.rows)));
pad_image(Rect((pad_image.cols - 1) - 2 * pad_cols + j, 0, 1,
pad_image.rows)).
copyTo(pad_image(Rect((pad_image.cols - 1) - j, 0, 1,
pad_image.rows)));
}
return pad_image;
}
// replicate padding
else if (type == "replicate")
{
for (int i = 0; i < pad_rows; i++)
{
scr(Rect(0, 0, scr.cols, 1)).copyTo(pad_image(Rect(pad_cols, i, scr.cols,
1)));
scr(Rect(0, (scr.rows - 1), scr.cols, 1)).copyTo(pad_image(Rect(pad_cols,
(pad_image.rows - 1) - i, scr.cols, 1)));
}
for (int j = 0; j < pad_cols; j++)
{
pad_image(Rect(pad_cols, 0, 1, pad_image.rows)).copyTo(pad_image(Rect(j,
0, 1, pad_image.rows)));
pad_image(Rect((pad_image.cols - 1) - pad_cols, 0, 1, pad_image.rows)).
copyTo(pad_image(Rect((pad_image.cols - 1) - j, 0, 1,
pad_image.rows)));
}
// zero padding
return pad_image;
}
else
{
return pad_image;
}
}
// member function to define kernels for convolution
Mat define_kernel(int k_width, int k_height, string type)
{
// box kernel
if (type == "box")
{
Mat kernel(k_height, k_width, CV_64FC1, Scalar(1.0 / (k_width * k_height)));
return kernel;
}
// gaussian kernel
else if (type == "gaussian")
{
// I will assume k = 1 and sigma = 1
int pad_rows = (k_height - 1) / 2;
int pad_cols = (k_width - 1) / 2;
Mat kernel(k_height, k_width, CV_64FC1);
for (int i = -pad_rows; i <= pad_rows; i++)
{
for (int j = -pad_cols; j <= pad_cols; j++)
{
kernel.at<double>(i + pad_rows, j + pad_cols) = exp(-(i*i + j*j) /
2.0);
}
}
kernel = kernel / sum(kernel);
return kernel;
}
}
public:
// member function to implement convolution
void convolve(Mat scr, Mat &dst, int k_w, int k_h, string paddingType, string
filterType)
{
Mat pad_img, kernel;
pad_img = padding(scr, k_w, k_h, paddingType);
kernel = define_kernel(k_w, k_h, filterType);
Mat output = Mat::zeros(scr.size(), CV_64FC1);
for (int i = 0; i < scr.rows; i++)
{
for (int j = 0; j < scr.cols; j++)
{
output.at<double>(i, j) = sum(kernel.mul(pad_img(Rect(j, i, k_w,
k_h)))).val[0];
}
}
output.convertTo(dst, CV_8UC1); //IplImage: IPL_DEPTH_8U Mat: CV_8UC1, CV_8UC2,
CV_8UC3, CV_8UC4
}
};
int main(){
Mat img, dst;
//
Load the image
img = imread("mrl.jpg", 0);
Mat kernel;
int k_w = 5; // kernel width
int k_h = 5; // kernel height
Filteration F1;
F1.convolve(img, dst, k_w, k_h, "gaussian", "box");
namedWindow("dst", WINDOW_AUTOSIZE);
imshow("dst", dst);
waitKey(0);
}

Related

Problem while copying an Opencv Image into a bigger one

I'm trying to copy an OpenCV image into a bigger image, but I'm getting some problems. I want to create a bigger image that has a border of an specific size but I do not want to change that border. So, I'd like to change just the center part of the image with the same size of it.
Here is the code I'm using.
main.cpp
#include <iostream>
#include "useful_tools.h"
int main()
{
Useful_Tools ut;
cv::Mat image = cv::imread("/home/felippe/Codigos_Cpp/Image_Registration_2D/square_landscape.jpeg");
cv::Mat gray_image(image.rows, image.cols, image.type());
cv::cvtColor(image, gray_image, cv::COLOR_BGR2GRAY);
cv::Mat sobel_x(3, 3, CV_64F);
//Filling the Sobel Filter
sobel_x.at<double>(0, 0) = -1;
sobel_x.at<double>(0, 1) = -2;
sobel_x.at<double>(0, 2) = -1;
sobel_x.at<double>(1, 0) = 0;
sobel_x.at<double>(1, 1) = 0;
sobel_x.at<double>(1, 2) = 0;
sobel_x.at<double>(2, 0) = 1;
sobel_x.at<double>(2, 1) = 2;
sobel_x.at<double>(2, 2) = 1;
cv::Mat edge = ut.correlation(gray_image, sobel_x, "zeros");
return 0;
}
function
cv::Mat PadImage(cv::Mat image, int k_cols, int k_rows)
{
//There is an error when k_rows or k_rows are even numbers.
//cv::Mat image_padded(image.rows + k_rows - 1, image.cols + k_cols - 1, image.type());
//Fixing:
cv::Mat image_padded(image.rows + (k_rows/2)*2, image.cols + (k_cols/2)*2, image.type());
image_padded = 0;
//if (!padding_type.compare("zeros"))
//{
//Naming conventions are: x applies cols, and y applies rows
//int x_add = k_rows / 2, y_add = k_cols / 2;
int y_add = k_rows / 2, x_add = k_cols / 2;
for (int i = y_add; i < image.rows + y_add; i++)
for (int j = x_add; j < image.cols + x_add; j++)
image_padded.at<double>(i, j) = image.at<double>(i - y_add, j - x_add);
//}
return image_padded;
}
cv::Mat Useful_Tools::correlation(cv::Mat image, cv::Mat kernel, std::string padding_type)
{
cv::Mat image_padded(image.rows + kernel.rows-1, image.cols + kernel.cols-1, image.type());
image_padded = 0;
cv::Mat result(image.rows, image.cols, image.type());
result = 0;
cv::Mat image_padded2 = PadImage(image, 3, 3);
showImage(image, "Original Image");
showImage(image_padded2, "Image Padded");
if (!padding_type.compare("zeros"))
{
int x_add = kernel.rows/2, y_add = kernel.cols/2;
for (int i = x_add; i < image.rows + x_add; i++)
for (int j = y_add; j < image.cols + y_add; j++)
image_padded.at<double>(i, j) = image.at<double>(i-x_add, j-y_add);
}
/*else if (!padding_type.compare("repeat"))
{
cv::Mat result(image.rows + kernel.rows/2, image.cols + kernel.cols/2, image.type());
for (int i = (kernel.rows-1)/2; i < image.rows + (kernel.rows-1)/2; i++)
for (int j = (kernel.cols-1)/2; j < image.cols + (kernel.cols-1)/2; j++)
result.at<double>(i, j) = image.at<double>(i-(kernel.rows-1)/2, j-((kernel.cols-1)/2));
}*/
else if (!padding_type.compare("without"))
{
image_padded.release();
cv::Mat image_padded = image;
}
else
std::cerr << "Please enter with a valid padding value." << std::endl;
//showImage(image_padded, "Testing Padding");
cv::imwrite( "Padding_image.jpg", image_padded);
for (int i = 0; i < result.rows; i++)
for (int j = 0; j < result.cols; j++)
for (int m = 0; m < kernel.rows; m++)
for (int n = 0; n < kernel.cols; n++)
{
std::cout << image_padded.at<double>(i+m+kernel.rows/2, j+n+kernel.cols/2) << std::endl
<< kernel.at<double>(m, n) << std::endl;
result.at<double>(i, j) += image_padded.at<double>(i+m+kernel.rows/2, j+n+kernel.cols/2)*kernel.at<double>(m, n);
std::cout << std::endl;
}
return result;
}
Here is the input image that I'm using.
Here is an example of image that I'm getting as a result.
I have done some other examples using vector and the result seems correct, so what is wrong with that code?
Thanks in advance.
The only issue I could find is in case kernel.rows or kernel.cols are even.
The size of your output image is (image.rows + kernel.rows-1, image.cols + kernel.cols-1).
Size is going to be too small when kernel.rows or kernel.cols are even.
For example: in case kernel.rows = 0, the size of the output is going to be smaller than the input.
Suggested correction:
cv::Mat image_padded(image.rows + (kernel.rows/2)*2, image.cols + (kernel.cols/2)*2, image.type());
Dividing the (integer) value by 2 and then multiply be 2 covers both even and odd cases.
Note about naming conversions:
The naming convention is: x applies cols, and y applies rows.
You have replaced the names, and made code reading difficult.
I am not sure if your issue is related to the problem I found.
It could also be a problem in the input or output (problem in the code parts you are not showing).
Here is a test sample code (I put some of your code in comments):
cv::Mat PadImage(cv::Mat image, int k_cols, int k_rows)
{
//There is an error when k_rows or k_rows are even numbers.
//cv::Mat image_padded(image.rows + k_rows - 1, image.cols + k_cols - 1, image.type());
//Fixing:
cv::Mat image_padded(image.rows + (k_rows/2)*2, image.cols + (k_cols/2)*2, image.type());
image_padded = 0;
//if (!padding_type.compare("zeros"))
//{
//Naming conventions are: x applies cols, and y applies rows
//int x_add = k_rows / 2, y_add = k_cols / 2;
int y_add = k_rows / 2, x_add = k_cols / 2;
for (int i = y_add; i < image.rows + y_add; i++)
for (int j = x_add; j < image.cols + x_add; j++)
image_padded.at<double>(i, j) = image.at<double>(i - y_add, j - x_add);
//}
return image_padded;
}
int main()
{
//Read input image as Grayscale (one byte per pixel).
cv::Mat Iu8 = cv::imread("img.png", cv::IMREAD_GRAYSCALE);
//Draw a white rectangle around the input image (for testing)
cv::rectangle(Iu8, cv::Rect(0, 0, Iu8.cols - 1, Iu8.rows - 1), cv::Scalar(255), 1);
cv::Mat I;
Iu8.convertTo(I, CV_64FC1); //Convert from uint8 to double
//Execute padding function
cv::Mat J = PadImage(I, 101, 0);
cv::Mat Ju8;
J.convertTo(Ju8, CV_8UC1); //Convert from double to uint8
//Display input and output
cv::imshow("Iu8", Iu8);
cv::imshow("Ju8", Ju8);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
Update
After posting your main, the problem can be found:
You are using at<double>, but image type is uchar (one byte per pixel).
Solution:
Replace at<double> with at<uchar> when reading from and writing to image.
Keep at<double> for the kernel, because the kernel type is double.
Here is the modified testing code (just for reference):
cv::Mat correlationPad(cv::Mat image, cv::Mat kernel, std::string padding_type)
{
cv::Mat image_padded(image.rows + kernel.rows - 1, image.cols + kernel.cols - 1, image.type());
image_padded = 0;
cv::Mat result(image.rows, image.cols, image.type());
result = 0;
//cv::Mat image_padded2 = PadImage(image, 3, 3);
//showImage(image, "Original Image");
//showImage(image_padded2, "Image Padded");
if (!padding_type.compare("zeros"))
{
int x_add = kernel.rows / 2, y_add = kernel.cols / 2;
for (int i = x_add; i < image.rows + x_add; i++)
for (int j = y_add; j < image.cols + y_add; j++)
image_padded.at<uchar>(i, j) = image.at<uchar>(i - x_add, j - y_add);
}
/*else if (!padding_type.compare("repeat"))
{
cv::Mat result(image.rows + kernel.rows/2, image.cols + kernel.cols/2, image.type());
for (int i = (kernel.rows-1)/2; i < image.rows + (kernel.rows-1)/2; i++)
for (int j = (kernel.cols-1)/2; j < image.cols + (kernel.cols-1)/2; j++)
result.at<double>(i, j) = image.at<double>(i-(kernel.rows-1)/2, j-((kernel.cols-1)/2));
}*/
else if (!padding_type.compare("without"))
{
//image_padded.release();
//cv::Mat image_padded = image;
}
else
std::cerr << "Please enter with a valid padding value." << std::endl;
//showImage(image_padded, "Testing Padding");
//cv::imwrite("Padding_image.jpg", image_padded);
//for (int i = 0; i < result.rows; i++)
// for (int j = 0; j < result.cols; j++)
// for (int m = 0; m < kernel.rows; m++)
// for (int n = 0; n < kernel.cols; n++)
// {
// std::cout << image_padded.at<double>(i + m + kernel.rows / 2, j + n + kernel.cols / 2) << std::endl
// << kernel.at<double>(m, n) << std::endl;
// result.at<double>(i, j) += image_padded.at<double>(i + m + kernel.rows / 2, j + n + kernel.cols / 2)*kernel.at<double>(m, n);
// std::cout << std::endl;
// }
return image_padded;
}
int main()
{
//Read input image as Grayscale (one byte per pixel).
cv::Mat image = cv::imread("square_landscape.jpeg");
cv::Mat gray_image(image.rows, image.cols, image.type());
cv::cvtColor(image, gray_image, cv::COLOR_BGR2GRAY);
//Draw a white rectangle around the input image (for testing)
//cv::rectangle(gray_image, cv::Rect(0, 0, gray_image.cols - 1, gray_image.rows - 1), cv::Scalar(255), 1);
cv::Mat sobel_x(3, 3, CV_64F);
//Filling the Sobel Filter
sobel_x.at<double>(0, 0) = -1;
sobel_x.at<double>(0, 1) = -2;
sobel_x.at<double>(0, 2) = -1;
sobel_x.at<double>(1, 0) = 0;
sobel_x.at<double>(1, 1) = 0;
sobel_x.at<double>(1, 2) = 0;
sobel_x.at<double>(2, 0) = 1;
sobel_x.at<double>(2, 1) = 2;
sobel_x.at<double>(2, 2) = 1;
cv::Mat edge = correlationPad(gray_image, sobel_x, "zeros");
cv::imwrite("edge.jpg", edge); //Save result.
//Display input and output
cv::imshow("gray_image", gray_image);
cv::imshow("edge", edge);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
edge:

Create a RGB image from pixel labels

Given a CV_32SC1 cv::Mat image that contains a label for each pixel (where a label is just an index in 0..N-1), what is the cleanest code in OpenCV to generate a CV_8UC3 image that shows each connected component with a different arbitrary color? If I don't have to specify the colors manually, as with cv::floodFill, the better.
If the max number of labels is 256, you can use applyColorMap, converting the image to CV_8U:
Mat1i img = ...
// Convert to CV_8U
Mat1b img2;
img.convertTo(img2, CV_8U);
// Apply color map
Mat3b out;
applyColorMap(img2, out, COLORMAP_JET);
If the number of labels is higher than 256, you need to do it yourself. Below there is an example that generates a JET colormap (it's based on Matlab implementation of the jet function). Then you can apply the colormap for each element of your matrix.
Please note that if you want a different colormap, or random colors, you just need to modify the //Create JET colormap part:
#include <opencv2/opencv.hpp>
#include <algorithm>
using namespace std;
using namespace cv;
void applyCustomColormap(const Mat1i& src, Mat3b& dst)
{
// Create JET colormap
double m;
minMaxLoc(src, nullptr, &m);
m++;
int n = ceil(m / 4);
Mat1d u(n*3-1, 1, double(1.0));
for (int i = 1; i <= n; ++i) {
u(i-1) = double(i) / n;
u((n*3-1) - i) = double(i) / n;
}
vector<double> g(n * 3 - 1, 1);
vector<double> r(n * 3 - 1, 1);
vector<double> b(n * 3 - 1, 1);
for (int i = 0; i < g.size(); ++i)
{
g[i] = ceil(double(n) / 2) - (int(m)%4 == 1 ? 1 : 0) + i + 1;
r[i] = g[i] + n;
b[i] = g[i] - n;
}
g.erase(remove_if(g.begin(), g.end(), [m](double v){ return v > m;}), g.end());
r.erase(remove_if(r.begin(), r.end(), [m](double v){ return v > m; }), r.end());
b.erase(remove_if(b.begin(), b.end(), [](double v){ return v < 1.0; }), b.end());
Mat1d cmap(m, 3, double(0.0));
for (int i = 0; i < r.size(); ++i) { cmap(int(r[i])-1, 2) = u(i); }
for (int i = 0; i < g.size(); ++i) { cmap(int(g[i])-1, 1) = u(i); }
for (int i = 0; i < b.size(); ++i) { cmap(int(b[i])-1, 0) = u(u.rows - b.size() + i); }
Mat3d cmap3 = cmap.reshape(3);
Mat3b colormap;
cmap3.convertTo(colormap, CV_8U, 255.0);
// Apply color mapping
dst = Mat3b(src.rows, src.cols, Vec3b(0,0,0));
for (int r = 0; r < src.rows; ++r)
{
for (int c = 0; c < src.cols; ++c)
{
dst(r, c) = colormap(src(r,c));
}
}
}
int main()
{
Mat1i img(1000,1000);
randu(img, Scalar(0), Scalar(10));
Mat3b out;
applyCustomColormap(img, out);
imshow("Result", out);
waitKey();
return 0;
}

Segmentation fault (core dumped) error while finding convolution of images in opencv c++

I am new to opencv. I am trying to do convolution of an image using kernel having same size as image in opencv c++. I am getting an error 'Segmentation fault (core dumped)'. I checked for intialisation of variables and for loop. But I am not able to sort out exactly where the problem is coming. Can anybody please help me in finding out the problem. My code is given below:
#include<opencv2/highgui/highgui.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include<stdio.h>
#include<iostream>
#include<math.h>
#include<cv.hpp>
using namespace cv;
using namespace std;
Mat img;
Mat kernel, gd, dest;
int c = 120;
double mysum = 0.0, mysum1 = 0.0, k = 0;
int cent=0,radius=0;
enum ConvolutionType {
/* Return the full convolution, including border */
CONVOLUTION_FULL,
/* Return only the part that corresponds to the original image */
CONVOLUTION_SAME,
/* Return only the submatrix containing elements that were not influenced by the
border
*/
CONVOLUTION_VALID
};
void conv2(const Mat &img, const Mat& kernel, ConvolutionType type,Mat& dest)
{
Mat source = img;
if(CONVOLUTION_FULL == type)
{
source = Mat();
const int additionalRows = kernel.rows - 1, additionalCols = kernel.cols - 1;
copyMakeBorder(img, source, (additionalRows + 1) / 2, additionalRows / 2,
(additionalCols + 1) / 2, additionalCols / 2, BORDER_CONSTANT, Scalar(0));
}
flip(kernel, kernel, -1);
Point anchor(kernel.cols - kernel.cols / 2 - 1, kernel.rows - kernel.rows / 2 - 1);
int borderMode = BORDER_CONSTANT;
filter2D(source, dest, img.depth(), kernel, anchor, 0, borderMode);
if(CONVOLUTION_VALID == type)
{
dest = dest.colRange((kernel.cols - 1) / 2, dest.cols - kernel.cols / 2).rowRange((kernel.rows - 1) / 2, dest.rows - kernel.rows / 2);
}
}
int main()
{
img = imread("building1.jpg", CV_LOAD_IMAGE_COLOR);
dest.create(img.size(), img.type());
gd.create(img.size(), img.type());
for(int j = 0; j < img.rows; j++)
{
for(int i = 0; i < img.cols; i++)
{
radius = ((cent - i)^2 + (cent - j)^2);
gd.at<float>(j, i) = exp((-(radius) / c^2));
mysum = mysum + gd.at<float>(j, i);
}
mysum1 = mysum1 + mysum;
}
k=1/mysum1;
cout<<endl<<k<<"\n"<<endl;
for(int j = 0; j < img.rows; j++)
{
for(int i = 0; i < img.cols; i++)
{
gd.at<float>(j, i) = k * gd.at<float>(j, i);
}
}
conv2(img, gd, CONVOLUTION_FULL, dest);
imshow("conv", dest);
waitKey(0);
return 0;
}
When you create img
img = imread("building1.jpg", CV_LOAD_IMAGE_COLOR);
it will of type CV_UC3, i.e 3 bytes per pixel (one each for blue, green and red).
However when you access the image
gd.at<float>(j, i) = k * gd.at<float>(j, i);
you are using a float pointer. Since a float is 4 bytes, rather than 3, you will end up accessing memory outside of the image, or even your program. The latter is happening, as indicated by the segmentation violation.
Probably the best thing to do would be to compile your code in debug mode. THen you will probably get an exception from OpenCV rather than the segmentation violation.
It looks like what you might have wanted is
img = imread("building1.jpg", CV_LOAD_IMAGE_GRAYSCALE);
img.convertTo(img, CV_32FC1);
...
Also some of you code can be greatly simplified, e.g.
for(int j = 0; j < img.rows; j++)
{
for(int i = 0; i < img.cols; i++)
{
gd.at<float>(j, i) = k * gd.at<float>(j, i);
}
}
should be
gd = gd * k;
If you are accessing pixels sequentially the using at<>() is very inefficient. See the efficient way

Error during convolution of image with a filter in opencv c++

I am new to opencv c++ .I am getting error with code for convolution (got from internet)which is equivalent to conv2 in matlab. The problem is all the pixel values are becoming 255.The filter which i am using in the code has same size as image. Can anybody please help me in correcting the problem.My opencv c++ code is given below:
#include<opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include<stdio.h>
#include<iostream>
#include<math.h>
#include<cv.hpp>
using namespace cv;
using namespace std;
Mat gd,img,bimgFiltered,gimgFiltered,rimgFiltered,fin_img;
Mat b,g,r,cr,cb,cg,B,G,R;
Mat b_logplane, b_plane,b_logfiltered,b_log,g_logplane,g_plane,g_logfiltered;
Mat g_log,r_logplane,r_plane,r_logfiltered,r_log;
Mat kernel, dest;
int m,n,m1,m2,n1,n2;
int c = 120;
double mysum = 0.0, mysum1 = 0.0, k = 0;
int cent=0,radius=0;
enum ConvolutionType {
/* Return the full convolution, including border */
CONVOLUTION_FULL,
/* Return only the part that corresponds to the original image */
CONVOLUTION_SAME,
/* Return only the submatrix containing elements that were not influenced
by the border
*/
CONVOLUTION_VALID
};
void conv2(const Mat &img, const Mat& kernel, ConvolutionType type,Mat& dest)
{
Mat source = img;
if(CONVOLUTION_FULL == type)
{
source = Mat();
const int additionalRows = kernel.rows - 1, additionalCols = kernel.cols - 1;
copyMakeBorder(img, source, (additionalRows + 1) / 2, additionalRows / 2,
(additionalCols + 1) / 2, additionalCols / 2, BORDER_CONSTANT, Scalar(0));
}
flip(kernel, kernel, -1);
Point anchor(kernel.cols - kernel.cols / 2 - 1, kernel.rows - kernel.rows / 2 - 1);
int borderMode = BORDER_CONSTANT;
filter2D(source, dest, img.depth(), kernel, anchor, 0, borderMode);
if(CONVOLUTION_VALID == type)
{
dest = dest.colRange((kernel.cols - 1) / 2, dest.cols - kernel.cols /
2).rowRange((kernel.rows - 1) / 2, dest.rows - kernel.rows / 2);
}
}
int main()
{
img = imread("milla.bmp", CV_LOAD_IMAGE_COLOR);
b.create(img.size(),img.type());
g.create(img.size(),img.type());
r.create(img.size(),img.type());
cr.create(img.size(),img.type());
cg.create(img.size(),img.type());
cb.create(img.size(),img.type());
Mat planes[3];
split(img,planes);
bimgFiltered.create(img.size(),img.type());
gimgFiltered.create(img.size(),img.type());
rimgFiltered.create(img.size(),img.type());
dest.create(img.size(), img.type());
gd.create(img.size(), img.type());
for(int j = 0; j < img.rows; j++)
{
for(int i = 0; i < img.cols; i++)
{
radius = ((cent - i)^2 + (cent - j)^2);
gd.at<float>(j, i) = exp((-(radius) / c^2));
mysum = mysum + gd.at<float>(j, i);
}
mysum1 = mysum1 + mysum;
}
k=1/mysum1;
cout<<endl<<k<<"\n"<<endl;
for(int j = 0; j < img.rows; j++)
{
for(int i = 0; i < img.cols; i++)
{
gd.at<float>(j, i) = k * gd.at<float>(j, i);
}
}
planes[0].convertTo(planes[0],CV_32F,1.0/255.0);
planes[1].convertTo(planes[1],CV_32F,1.0/255.0);
planes[2].convertTo(planes[2],CV_32F,1.0/255.0);
conv2(planes[0],gd,CONVOLUTION_SAME,bimgFiltered);
conv2(planes[1],gd,CONVOLUTION_SAME,gimgFiltered);
conv2(planes[2],gd,CONVOLUTION_SAME,rimgFiltered);
imshow("img",gimgFiltered );
waitKey(0);
return 0;
}
There are a few problems with the code:
Issue 1:
In the following two lines:
radius = ((cent - i)^2 + (cent - j)^2);
gd.at<float>(j, i) = exp((-(radius) / c^2));
You are using ^ operator which is the bitwise XOR operator in C/C++. I think you are mistaking it for power operator. To take the power of a number you have to use the pow function as follows:
radius = powf((cent - i),2) + powf((cent - j),2);
gd.at<float>(j, i) = expf((-(radius) / (c*c)));
Issue 2:
The gd matrix is assumed to have floating point values as it is accessed like gd.at<float>(j, i), but it is declared with the same type as that of the image, i.e. CV_8UC3. So gd should be created as follows:
gd.create(img.size(), CV_32FC1);
Issue 3:
Another possible logical error may be present in the first nested loop. You may have to set mysum = 0; before starting the inner loop like this:
for(int j = 0; j < img.rows; j++)
{
mysum = 0;
for(int i = 0; i < img.cols; i++)
{
radius = powf((cent - i),2) + powf((cent - j),2);
gd.at<float>(j, i) = expf((-(radius) / (c*c)));
mysum = mysum + gd.at<float>(j, i);
}
mysum1 = mysum1 + mysum;
}
Issue 4:
Output filtered images should be created single channel instead of 3 channels:
bimgFiltered.create(img.size(),CV_8UC1);
gimgFiltered.create(img.size(),CV_8UC1);
rimgFiltered.create(img.size(),CV_8UC1);

Histogram of oriented gradiants

For a project I'm writing some code to compute the HoG of some images, but I'm stuck with the fact that my orientations are only between 0 ~ 90 degrees, while using the atan2 function.
I'm guessing that this problem occurs due to the filter2D function of OpenCV but I'm not sure if this is the reason or that I'm doing something else wrong:
Vector<Vector<Mat_<float>>> HoG(Mat image) {
Mat img_x;
Mat img_y;
IplImage img = image;
Mat kern_x = (Mat_<char>(1, 3) << -1, 0, 1);
Mat kern_y = (Mat_<char>(3, 1) << -1, 0, 1);
filter2D(image, img_x, image.depth(), kern_x);
filter2D(image, img_y, image.depth(), kern_y);
Vector<Vector<Mat_<float>>> histograms;
for(int y = 0; y < image.rows - size; y += size) {
Vector<Mat_<float>> temp_hist;
for(int x = 0; x < image.cols - size; x += size) {
float total_mag = 0;
Mat hist = Mat::zeros(1, 8, CV_32FC1);
for(int i = y; i < y + size; ++i) {
for(int j = x; j < x + size; ++j) {
float grad_x = (float)img_x.at<uchar>(i, j);
float grad_y = (float)img_y.at<uchar>(i, j);
double ori = myatan2(grad_x, grad_y);
float mag = sqrt(pow(grad_x, 2) + pow(grad_y, 2));
int bin = round(ori/45);
hist.at<float>(0, (bin - 1 < 0 ? 7 : bin - 1)) += - (float)(ori - ((round(ori/45) - 1) * 45.0 + 22.5)) / 45.0f;
hist.at<float>(0, bin) += -(float)(ori - ((round(ori/45) - 1) * 45.0 + 22.5)) / 45.0f;
total_mag += mag;
}
}
// Normalize the histogram
for(int i = 0; i < 8; ++i) {
hist.at<float>(0, i) = hist.at<float>(0, i) / total_mag;
}
temp_hist.push_back(hist);
}
histograms.push_back(temp_hist);
}
return histograms;
}
If you have any other tips to increase a speed-up in my code or something else that is also welcome of course.
I notice this:
float grad_x = (float)img_x.at<uchar>(i, j);
float grad_y = (float)img_y.at<uchar>(i, j);
You seem to be using uchar. Should this not be char?