写作参考
(1)OpenCV
(2)OpenCV中文论坛
(3)Richard Szeliski写的Computer Vision: Algorithms and Applications
我对Opencv平滑的理解
(1)就一幅数字图像来说,大多区域内都是较为平坦的,如果领域内有一两个点像素突然变化,一般认为是噪点。如果一些连续相近的点变化,认为是边缘。
(2)而滤波旨在消除噪声对于图像的影响,例如取中值或者均值不就能较好的消除椒盐噪声的影响吗?但是问题又来了,这样边缘的变化不就也被模糊了吗?所以就用到双边滤波(Bilateral Filter)。
一,中值滤波
即:用中值像素代替核内所有像素。
ksize为正方形核大小,必须为奇数。如果为偶数,哪来的中值?
二,均值滤波(归一化滤波)
即:用核内所有像素的均值替换核内所有像素值。
归一化滤波的加权公式,如下图所示(均值滤波公式):
三,高斯滤波
高斯内核函数:
1-D分布(1-D Gaussian distribution with mean 0 and =1):
2-D分布(2-D Gaussian distribution with mean (0,0) and =1):
例如3*3高斯内核模板:
应用:
(1)高斯滤波后图像被平滑的程度取决于标准差。它的输出是领域像素的加权平均,同时离中心越近的像素权重越高。因此,相对于均值滤波(mean filter)它的平滑效果更柔和,而且边缘保留的也更好。
(2)高斯滤波被用作为平滑滤波器的本质原因是因为它是一个低通滤波器,见下图。而且,大部分基于卷积平滑滤波器都是低通滤波器
Matlab函数:
h = fspecial('gaussian', hsize, sigma) returns a rotationally symmetric Gaussian lowpass filter of size hsize with standard deviation sigma (positive). hsize can be a vector specifying the number of rows and columns in h, or it can be a scalar, in which case h is a square matrix.
The default value for hsize is [3 3]; the default value for sigma is 0.5.
四,双边滤波
双边滤波就会很好的保留边缘信息,具体附上一个链接:
http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html
static void exchange(int& a, int& b)
{
int t = 0;
t = a;
a = b;
b = t;
}
static void bubble_sort(int* K, int lenth)
{
for (int i = 0; i < lenth; i++)
for (int j = i + 1; j < lenth; j++)
{
if (K[i]>K[j])
exchange(K[i], K[j]);
}
}
///产生二维的高斯内核
static cv::Mat generate_gassian_kernel(double u, double sigma, cv::Size size)
{
int width = size.width;
int height = size.height;
cv::Mat gassian_kernel(cv::Size(width, height), CV_64FC1);
double sum = 0;
double sum_sum = 0;
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
{
sum = 1.0 / 2.0 / CV_PI / sigma / sigma * exp(-1.0 * ((i - width / 2)*(i - width / 2) + (j - width / 2)*(j - width / 2)) / 2.0 / sigma / sigma);
sum_sum += sum;
gassian_kernel.ptr(i)[j] = sum;
}
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
{
gassian_kernel.ptr(i)[j] /= sum_sum;
}
return gassian_kernel;
}
///均值滤波
void lmt_main_blur(cv::Mat& img_in, cv::Mat& img_out, int kernel_size)
{
img_out = img_in.clone();
cv::Mat mat1;
cv::copyMakeBorder(img_in, mat1, kernel_size, kernel_size, kernel_size, kernel_size, cv::BORDER_REPLICATE);
int cols = mat1.cols;
int rows = mat1.rows;
int channels = img_out.channels();
const uchar* const pt = mat1.ptr(0);
uchar* pt_out = img_out.ptr(0);
for (int i = kernel_size; i < rows - kernel_size; i++)
{
for (int j = kernel_size; j < cols - kernel_size; j++)
{
if (channels == 1)
{
long long int sum_pixel = 0;
for (int m = -1 * kernel_size; m < kernel_size; m++)
for (int n = -1 * kernel_size; n < kernel_size; n++)
{
sum_pixel += pt[(i + m)*cols + (j + n)];
}
img_out.ptr(i - kernel_size)[j - kernel_size] = (double)sum_pixel / (kernel_size*kernel_size * 4);
}
else if (channels == 3)
{
long long int sum_pixel = 0;
long long int sum_pixel1 = 0;
long long int sum_pixel2 = 0;
for (int m = -1 * kernel_size; m < kernel_size; m++)
for (int n = -1 * kernel_size; n < kernel_size; n++)
{
sum_pixel += pt[((i + m)*cols + (j + n))*channels + 0];
sum_pixel1 += pt[((i + m)*cols + (j + n))*channels + 1];
sum_pixel2 += pt[((i + m)*cols + (j + n))*channels + 2];
}
img_out.ptr(i - kernel_size)[(j - kernel_size)*channels + 0] = (double)sum_pixel / (double)(kernel_size*kernel_size * 4);
img_out.ptr(i - kernel_size)[(j - kernel_size)*channels + 1] = (double)sum_pixel1 / (double)(kernel_size*kernel_size * 4);
img_out.ptr(i - kernel_size)[(j - kernel_size)*channels + 2] = (double)sum_pixel2 / (double)(kernel_size*kernel_size * 4);
}
}
}
}
///中值滤波
void lmt_median_blur(cv::Mat& img_in, cv::Mat& img_out, int kernel_size)
{
img_out = img_in.clone();
cv::Mat mat1;
cv::copyMakeBorder(img_in, mat1, kernel_size, kernel_size, kernel_size, kernel_size, cv::BORDER_REPLICATE);
int cols = mat1.cols;
int rows = mat1.rows;
int channels = img_out.channels();
cv::Mat mat[3];
cv::Mat mat_out[3];
cv::split(mat1, mat);
cv::split(img_out, mat_out);
for (int k = 0; k < 3; k++)
{
const uchar* const pt = mat[k].ptr(0);
uchar* pt_out = mat_out[k].ptr(0);
for (int i = kernel_size; i < rows - kernel_size; i++)
{
for (int j = kernel_size; j < cols - kernel_size; j++)
{
long long int sum_pixel = 0;
int* K = new int[kernel_size*kernel_size * 4];
int ker_num = 0;
for (int m = -1 * kernel_size; m < kernel_size; m++)
for (int n = -1 * kernel_size; n < kernel_size; n++)
{
K[ker_num] = pt[(i + m)*cols + (j + n)];
ker_num++;
}
bubble_sort(K, ker_num);
mat_out[k].ptr(i - kernel_size)[j - kernel_size] = K[ker_num / 2];
}
}
}
cv::merge(mat_out, 3, img_out);
}
///高斯滤波
void lmt_gaussian_blur(cv::Mat& img_src, cv::Mat& img_dst, cv::Size kernel_size)
{
img_dst = cv::Mat(cv::Size(img_src.cols, img_src.rows), img_src.type());
int cols = img_src.cols;
int rows = img_src.rows;
int channels = img_src.channels();
cv::Mat gassian_kernel = generate_gassian_kernel(0, 1, kernel_size);
int width = kernel_size.width / 2;
int height = kernel_size.height / 2;
for (int i = height; i < rows - height; i++)
{
for (int j = width; j < cols - width; j++)
{
for (int k = 0; k < channels; k++)
{
double sum = 0.0;
for (int m = -height; m <= height; m++)
{
for (int n = -width; n <= width; n++)
{
sum += (double)(img_src.ptr(i + m)[(j + n)*channels + k]) * gassian_kernel.ptr(height + m)[width + n];
}
}
if (sum > 255.0)
sum = 255;
if (sum < 0.0)
sum = 0;
img_dst.ptr(i)[j*channels + k] = (uchar)sum;
}
}
}
}
///双边滤波
void lmt_bilateral_filter(cv::Mat& img_in, cv::Mat& img_out, const int r, double sigma_d, double sigma_r)
{
int i, j, m, n, k;
int nx = img_in.cols, ny = img_in.rows, m_nChannels = img_in.channels();
const int w_filter = 2 * r + 1; // 滤波器边长
double gaussian_d_coeff = -0.5 / (sigma_d * sigma_d);
double gaussian_r_coeff = -0.5 / (sigma_r * sigma_r);
double **d_metrix = new double *[w_filter];
for (int i = 0; i < w_filter; ++i)
d_metrix[i] = new double[w_filter];
double r_metrix[256]; // similarity weight
img_out = cv::Mat(img_in.size(),img_in.type());
uchar* m_imgData = img_in.ptr(0);
uchar* m_img_outData = img_out.ptr(0);
// copy the original image
double* img_tmp = new double[m_nChannels * nx * ny];
for (i = 0; i < ny; i++)
for (j = 0; j < nx; j++)
for (k = 0; k < m_nChannels; k++)
{
img_tmp[i * m_nChannels * nx + m_nChannels * j + k] = m_imgData[i * m_nChannels * nx + m_nChannels * j + k];
}
// compute spatial weight
for (i = -r; i <= r; i++)
for (j = -r; j <= r; j++)
{
int x = j + r;
int y = i + r;
d_metrix[y][x] = exp((i * i + j * j) * gaussian_d_coeff);
}
// compute similarity weight
for (i = 0; i < 256; i++)
{
r_metrix[i] = exp(i * i * gaussian_r_coeff);
}
// bilateral filter
for (i = 0; i < ny; i++)
for (j = 0; j < nx; j++)
{
for (k = 0; k < m_nChannels; k++)
{
double weight_sum, pixcel_sum;
weight_sum = pixcel_sum = 0.0;
for (m = -r; m <= r; m++)
for (n = -r; n <= r; n++)
{
if (m*m + n*n > r*r) continue;
int x_tmp = j + n;
int y_tmp = i + m;
x_tmp = x_tmp < 0 ? 0 : x_tmp;
x_tmp = x_tmp > nx - 1 ? nx - 1 : x_tmp; // 边界处理,replicate
y_tmp = y_tmp < 0 ? 0 : y_tmp;
y_tmp = y_tmp > ny - 1 ? ny - 1 : y_tmp;
int pixcel_dif = (int)abs(img_tmp[y_tmp * m_nChannels * nx + m_nChannels * x_tmp + k] - img_tmp[i * m_nChannels * nx + m_nChannels * j + k]);
double weight_tmp = d_metrix[m + r][n + r] * r_metrix[pixcel_dif]; // 复合权重
pixcel_sum += img_tmp[y_tmp * m_nChannels * nx + m_nChannels * x_tmp + k] * weight_tmp;
weight_sum += weight_tmp;
}
pixcel_sum = pixcel_sum / weight_sum;
m_img_outData[i * m_nChannels * nx + m_nChannels * j + k] = (uchar)pixcel_sum;
} // 一个通道
} // END ALL LOOP
for (i = 0; i < w_filter; i++)
delete[] d_metrix[i];
delete[] d_metrix;
}