Gaussian Blur image processing c++ - c++

after trying to implement a Gaussian blur for an image i have ran into a problem where the output image looks like multiple blurred versions of the original image (input image)
I have too low of a reputation to post images so have no idea how to fully show you what is happening however, i can post a gyazo link to the image:
https://gyazo.com/38fbe1abd442a3167747760866584655 - Original,
https://gyazo.com/471693c49917d3d3e243ee4156f4fe12 - Output
Here is some code:
int kernel[3][3] = { 1, 2, 1,
2, 4, 2,
1, 2, 1 };
void guassian_blur2D(unsigned char * arr, unsigned char * result, int width, int height)
{
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
for (int k = 0; k < 3; k++)
{
result[3 * row * width + 3 * col + k] = accessPixel(arr, col, row, k, width, height);
}
}
}
}
int accessPixel(unsigned char * arr, int col, int row, int k, int width, int height)
{
int sum = 0;
int sumKernel = 0;
for (int j = -1; j <= 1; j++)
{
for (int i = -1; i <= 1; i++)
{
if ((row + j) >= 0 && (row + j) < height && (col + i) >= 0 && (col + i) < width)
{
int color = arr[(row + j) * 3 * width + (col + i) * 3 + k];
sum += color * kernel[i + 1][j + 1];
sumKernel += kernel[i + 1][j + 1];
}
}
}
return sum / sumKernel;
}
Image is saved:
guassian_blur2D(inputBuffer, outputBuffer, width, height);
//Save the processed image
outputImage.convertToType(FREE_IMAGE_TYPE::FIT_BITMAP);
outputImage.convertTo24Bits();
outputImage.save("appleBlur.png");
cout << "Blur Complete" << endl;
Any help would be great, if this also helps i am trying to store the image as a grey-scale so that no colour is saved.

Looks like the problem is not within your blurring code, and is related to saving or accessing image data.
I have used OpenCV to read/save images, and got expected result. Here's a snippet:
cv::Mat3b img = cv::imread("path_to_img.png");
cv::Mat3b out = img.clone();
guassian_blur2D(img.data, out.data, img.cols, img.rows);
cv::imshow("img", img);
cv::imshow("out", out);
cv::waitKey(0);
And here are input and output images:
The blur is not very noticeable (due to high image resolution and small kernel), but if you look carefully - it looks correct.

Related

gaussian smoothing output misaligned

I am trying to perform gaussian smoothing on this image without using any opencv function (except displaying the image).
However, the output I got after convoluting the image with the gaussian kernel is as follow:
The output image seems to have misaligned and looks very weird. Any idea what is happening?
Generate gaussian kernel:
double gaussian(int x, int y,double sigma){
return (1/(2*M_PI*pow(sigma,2)))*exp(-1*(pow(x,2)+pow(y,2))/(2*pow(sigma,2)));
}
double generateFilter(vector<vector<double>> & kernel,int width,double sigma){
int value = 0;
double total =0;
if(width%2 == 1){
value = (width-1)/2;
}else{
value = width/2;
}
double smallest = gaussian(-1*value,-1*value,sigma);
for(int i = -1*value; i<=value; i++){
vector<double> temp;
for(int k = -1*value; k<=value; k++){
int gVal = round(gaussian(i,k,sigma)/smallest);
temp.push_back(gVal);
total += gVal;
}
kernel.push_back(temp);
}
cout<<total<<endl;
return total;
}
Convolution:
vector<vector<unsigned int>> convolution(vector<vector<unsigned int>> src, vector<vector<double>> kernel,double total){
int kCenterX = floor(kernel.size() / 2); //center of kernel
int kCenterY = kCenterX; //center of kernel
int kRows = kernel.size(); //height of kernel
int kCols = kRows; //width of kernel
int imgRows = src.size(); //height of input image
int imgCols = src[0].size(); //width of input image
vector<vector<unsigned int>> dst = vector<vector<unsigned int>> (imgRows, vector<unsigned int>(imgCols ,0));
for ( size_t row = 0; row < imgRows; row++ ) {
for ( size_t col = 0; col < imgCols; col++ ) {
float accumulation = 0;
float weightsum = 0;
for ( int i = -1*kCenterX; i <= 1*kCenterX; i++ ) {
for ( int j = -1*kCenterY; j <= 1*kCenterY; j++ ) {
int k = 0;
if((row+i)>=0 && (row+i)<imgRows && (col+j)>=0 && (col+j)<imgCols){
k = src[row+i][col+j];
weightsum += kernel[kCenterX+i][kCenterY+j];
}
accumulation += k * kernel[kCenterX +i][kCenterY+j];
}
}
dst[row][col] = round(accumulation/weightsum);
}
}
return dst;
}
Thank you.
The convolution function is basically correct, so the issue is with the input and output format.
Make sure you are reading the image as Grayscale (and not RGB):
cv::Mat I = cv::imread("img.png", cv::IMREAD_GRAYSCALE);
You are passing vector<vector<unsigned int>> argument to convolution.
I can't say if it's part of the problem or not, but it's recommended to pass argument of type cv::Mat (and return cv::Mat):
cv::Mat convolution(cv::Mat src, vector<vector<double>> kernel, double total)
I assume you can convert the input to and from vector<vector<unsigned int>>, but it's not necessary.
Here is a working code sample:
#include <vector>
#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
double gaussian(int x, int y, double sigma) {
return (1 / (2 * 3.141592653589793*pow(sigma, 2)))*exp(-1 * (pow(x, 2) + pow(y, 2)) / (2 * pow(sigma, 2)));
}
double generateFilter(vector<vector<double>> & kernel, int width, double sigma)
{
int value = 0;
double total = 0;
if (width % 2 == 1) {
value = (width - 1) / 2;
}
else {
value = width / 2;
}
double smallest = gaussian(-1 * value, -1 * value, sigma);
for (int i = -1 * value; i <= value; i++) {
vector<double> temp;
for (int k = -1 * value; k <= value; k++) {
int gVal = round(gaussian(i, k, sigma) / smallest);
temp.push_back(gVal);
total += gVal;
}
kernel.push_back(temp);
}
cout << total << endl;
return total;
}
//vector<vector<unsigned int>> convolution(vector<vector<unsigned int>> src, vector<vector<double>> kernel, double total) {
cv::Mat convolution(cv::Mat src, vector<vector<double>> kernel, double total) {
int kCenterX = floor(kernel.size() / 2); //center of kernel
int kCenterY = kCenterX; //center of kernel
int kRows = kernel.size(); //height of kernel
int kCols = kRows; //width of kernel
int imgRows = src.rows;//src.size(); //height of input image
int imgCols = src.cols;//src[0].size(); //width of input image
//vector<vector<unsigned int>> dst = vector<vector<unsigned int>> (imgRows, vector<unsigned int>(imgCols ,0));
cv::Mat dst = cv::Mat::zeros(src.size(), CV_8UC1); //Create destination matrix, and fill with zeros (dst is Grayscale image with byte per pixel).
for (size_t row = 0; row < imgRows; row++) {
for (size_t col = 0; col < imgCols; col++) {
double accumulation = 0;
double weightsum = 0;
for (int i = -1 * kCenterX; i <= 1 * kCenterX; i++) {
for (int j = -1 * kCenterY; j <= 1 * kCenterY; j++) {
int k = 0;
if ((row + i) >= 0 && (row + i) < imgRows && (col + j) >= 0 && (col + j) < imgCols) {
//k = src[row+i][col+j];
k = (int)src.at<uchar>(row + i, col + j); //Read pixel from row [row + i] and column [col + j]
weightsum += kernel[kCenterX + i][kCenterY + j];
}
accumulation += (double)k * kernel[kCenterX + i][kCenterY + j];
}
}
//dst[row][col] = round(accumulation/weightsum);
dst.at<uchar>(row, col) = (uchar)round(accumulation / weightsum); //Write pixel from to row [row] and column [col]
//dst.at<uchar>(row, col) = src.at<uchar>(row, col);
}
}
return dst;
}
int main()
{
vector<vector<double>> kernel;
double total = generateFilter(kernel, 11, 3.0);
//Read input image as Grayscale (one byte per pixel).
cv::Mat I = cv::imread("img.png", cv::IMREAD_GRAYSCALE);
cv::Mat J = convolution(I, kernel, total);
//Display input and output
cv::imshow("I", I);
cv::imshow("J", J);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
Result:

Traverse Mat as 64x64 per iteration

Good day,
I am looking for a nested for loop to traverse the image of size 512x512 as 64x64 per iteration. My goal is to determine the element of each sub-region, such as performing number of edge count.
In this following code, I have tried to iterate per 64 row and 64 col (expect 8 times each to hit 512). Within the nested for loop, I have placed vec3b as a test run and I aware that the entire cycle of my code is repeating an identical pattern rather than traverse entire image.
int main()
{
char imgName[] = "data/near.jpg"; //input1.jpg, input2.jpg, near.jpg, far.jpg
Mat sourceImage = imread(imgName);
resize(sourceImage, sourceImage, Size(512, 512));
for (int t_row = 0; t_row < sourceImage.rows; t_row += 64)
{
for (int t_col = 0; t_col < sourceImage.cols; t_col += 64)
{
for (int row = 0; row < 64; row++)
{
for (int col = 0; col < 64; col++)
{
Vec3b bgrPixel = sourceImage.at<Vec3b>(row, col);
cout << bgrPixel << endl;
}
}
}
}
return 0;
}
If you actually want to have 64x64 sub-images per iteration, make use of OpenCV's Rect, like so:
const int w = 64;
const int h = 64;
for (int i = 0; i < int(sourceImage.size().width / w); i++)
{
for (int j = 0; j < int(sourceImage.size().height / h); j++)
{
cv::Mat smallImage = sourceImage(cv::Rect(i * w, j * h, w, h));
// Pass smallImage to any function...
}
}
You are iterating over
Vec3b bgrPixel = sourceImage.at<Vec3b>(row, col);
with 0 <= row < 64 and 0 <= col < 64. You are right that you iterate 64 times over the same region.
It should be
Vec3b bgrPixel = sourceImage.at<Vec3b>(t_row + row, t_col + col);

Edge detection - bad detection

Hello you all and thank you
I am a student and I am writing a c++ code to my final project.
My code problem is with Edge Detection algorithm (Image processing),
while i am running Edge Detection algorithm in MATLAB I gets a good Edge detection, but if I am running the algorithm code written in c++, the created picture is with bad detection.
I tried to detect the edge with Matlb by using threshold of 0.03 and the detection was great( the changes in my project is very low (little changes on white surface).
Thank you so much
Idan.
maybe someone can help me, this is my code:
void ApplySobelFilter(unsigned char src[][NUMBER_OF_COLUMNS], float Threshold)
{
unsigned char dst[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
unsigned char * ptrToImage;
ptrToImage = dst[0];
// Kernels for sobel operator
int Kernel_X[3][3] = { { -1, 0, 1 },{ -2, 0, 2 },{ -1, 0, 1 } };
int Kernel_Y[3][3] = { { 1, 2, 1 },{ 0, 0, 0 },{ -1, -2, -1 } };
// clears destination image
for (int pixel = 0; pixel < NUMBER_OF_ROWS*NUMBER_OF_COLUMNS; pixel++)
*ptrToImage++ = 0;
for (int row = 1; row < NUMBER_OF_ROWS - 1; row++)
for (int column = 1; column < NUMBER_OF_COLUMNS - 1; column++)
{
double Gtot = 0;
int Gx = 0;
int Gy = 0;
for (int x = -1; x <= 1; x++)
for (int y = -1; y <= 1; y++)
{
Gx += src[row + y][column + x] * Kernel_X[y + 1][x + 1];
Gy += src[row + y][column + x] * Kernel_Y[y + 1][x + 1];
}
Gtot = sqrt(double(Gx ^ 2 + Gy ^ 2));
if (Gtot >= Threshold)
dst[row][column] = 255;
else
dst[row][column] = 0;
}
for (int row = 0; row < NUMBER_OF_ROWS; row++)
{
for (int col = 0; col < NUMBER_OF_COLUMNS; col++)
{
src[row][col] = dst[row][col];
}
}
}
Gtot = sqrt(double(Gx ^ 2 + Gy ^ 2));
That's probably not doing what you expect. The operator ^ computes bit-wise xor, not power. In your case it simply flips the second bit of Gx and Gy. Squaring the variables can be done e.g. like this:
Gtot = sqrt(double(Gx * Gx + Gy * Gy));

Error in Kernel launch statement in CUDA

I am doing a small project in image processing using CUDA.I am trying to use Gaussian blurring to blur an image.Everything is fine but I cannot figure out why the kernel launch statement is showing this strange error:
Here is my complete code, if it can be of any help:
#include<time.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<cuda_runtime.h>
#include<device_launch_parameters.h>
#include <helper_cuda.h>
#include <helper_cuda_gl.h>
#include<helper_image.h>
#include< helper_cuda_gl.h>
#include<helper_cuda_drvapi.h>
unsigned int width, height;
int mask[3][3] = { 1, 2, 1,
2, 3, 2,
1, 2, 1,
};
int getPixel(unsigned char *arr, int col, int row)
{
int sum = 0;
for (int j = -1; j <= 1; j++)
{
for (int i = -1; i <= 1; i++)
{
int color = arr[(row + j)*width + (col + i)];
sum += color*mask[i + 1][j + 1];
}
}
return sum / 15;
}
void h_blur(unsigned char * arr, unsigned char * result){
int offset = 2 * width;
for (int row = 2; row < height - 3; row++)
{
for (int col = 2; col < width - 3; col++)
{
result[offset + col] = getPixel(arr, col, row);
}
offset += width;
}
}
__global__ void d_blur(unsigned char *arr, unsigned char * result, int width, int height)
{
int col = blockIdx.x*blockDim.x + threadIdx.x;
int row = blockIdx.y*blockDim.y + threadIdx.y;
if (row < 2 || col < 2 || row >= height - 3 || col >= width - 3)
return;
int mask[3][3] = { 1, 2, 1, 2, 3, 2, 1, 2, 1 };
int sum = 0;
for (int j = -1; j <= 1; j++)
{
int color = arr[(row + j)*width + (col + i)];
sum += color*mask[i + 1][j + 1];
}
result[row*width + col] = sum / 15;
}
int main(int argc, char ** argv)
{
unsigned char *d_resultPixels;
unsigned char *h_resultPixels;
unsigned char *h_pixels = NULL;
unsigned char *d_pixels = NULL;
char *srcPath = "C:\ProgramData\NVIDIA Corporation\CUDA Samples\v6.5\3_Imaging\dxtc\data\lena_std.ppm";
char *h_ResultPath = "C:\ProgramData\NVIDIA Corporation\CUDA Samples\v6.5\3_Imaging\dxtc\data\lena_std.ppm";
char *d_ResultPath = "C:\ProgramData\NVIDIA Corporation\CUDA Samples\v6.5\3_Imaging\dxtc\data\lena_std.ppm";
sdkLoadPGM(srcPath, &h_pixels, &width, &height);
int ImageSize = sizeof(unsigned char) * width * height;
h_resultPixels = (unsigned char *)malloc(ImageSize);
cudaMalloc((void**)&d_pixels, ImageSize);
cudaMalloc((void**)&d_resultPixels, ImageSize);
cudaMemcpy(d_pixels, h_pixels, ImageSize, cudaMemcpyHostToDevice);
dim3 block(16, 16);
dim3 grid(width / 16, height / 16);
d_blur << < grid, block >> >(d_pixels, d_resultPixels, width, height);
cudaThreadSynchronize();
cudaMemcpy(h_resultPixels, d_resultPixels, ImageSize, cudaMemcpyDeviceToHost);
sdkSavePGM(d_ResultPath, h_resultPixels, width, height);
printf("Press enter to exit ...\n");
getchar();
}
As you are trying to run this in Visual Studio, you need to update the Intellisense. Also,you can refer the following link for a better Image Convolution Operation in CUDA.
2D Image Convolution in CUDA

Flip an image vertically

I'm trying to flip an image vertically, after retrieving the buffer from openGL. It seems to be outputting an incorrect image with the following code:
const int width = 100;
const int height = width;
const int components = 3;
unsigned char pixels[width * height * components];
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
unsigned char flipPixels[width * height * components];
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
for (int k = 0; k < components; ++k) {
flipPixels[i + j * width + k] = pixels[(height) * (width) - ((j+1) * width) + i + k];
}
}
}
I know I can only iterate half the height and achieve the same, but I want to implement it by going through the complete height of the image. I can't seem to figure out what's wrong with the code. Any help would be appreciated.
I'm not sure how the image is stored but your indices i and k are given the same stride which is suspicious. Maybe you want i * components and j * width * components. After that, inverting vertically you should only have to change j to (height - j - 1).
flipPixels[(i + j * width) * components + k] = pixels[(i + (height - 1 - j) * width) * components + k];
I had the same issue, the pixels returned by OpenGL resulten in an upside down bitmap. so I flipped them like this: but the bitmap is still flipped left to right...
void Flip(GLubyte* pixels, int pixelbuffersize)
{
// basically rewrites from bottom up...
std::vector<GLubyte> flipped_pixels(pixels, pixels+pixelbuffersize);
auto count = flipped_pixels.size();
std::reverse(flipped_pixels.begin(), flipped_pixels.end());
GLubyte* buff = (reinterpret_cast<GLubyte*>(&flipped_pixels[0]));
const void * pnewdata = (const void *)buff;
memcpy(pixels, pnewdata, count);
}