进行高斯卷积 (2d) 时边界处的急剧过渡
Sharp transition at borders when doing Gaussian Convolution (2d)
我正在尝试使用高斯二维卷积来模糊矩阵。
但是我在边界元素处得到了急剧的转变。
这是我的一段代码 运行:
// create 1D Kernel
void createGaussianKerenel_1D() {
unsigned kernelSize = 2 * kernelRad_ + 1;
gaussian1Dkernel_ = vector<double>(kernelSize);
double sigma = (double)kernelRad_;
double sum = 0.0;
for(unsigned i = 0; i < kernelSize; ++i) {
gaussian1Dkernel_[i] = gaussian(i, sigma);
sum += gaussian1Dkernel_[i];
}
// normalize
for(unsigned i = 0; i < kernelSize; ++i) {
gaussian1Dkernel_[i] /= sum;
cout << gaussian1Dkernel_[i] << endl;
}
}
// gaussian function
double gaussian(unsigned int i, double sigma) const {
double x = ((double)i - (double)kernelRad_) / sigma;
return exp(-x * x / 2);
}
// do Separable 2D Convolution (in place)
// my initialMatrix_ is of yn_ x xn_ size
void getBlurredThermalMap() {
assert(!gaussian1Dkernel_.empty());
vector<vector<double> > tmpMatrix(yn_);
unsigned kernelSize = 2 * kernelRad_ + 1;
// in x direction
for(unsigned i = 0; i < yn_; ++i) {
for(unsigned j = 0; j < xn_; ++j) {
double approxVal = 0.0;
for(unsigned row = 0; row < kernelSize; ++row) {
unsigned neighbor_j = j + row - kernelRad_;
// ignore values that are out of bound
if(neighbor_j >= 0 && neighbor_j < xn_) {
approxVal += initialMatrix_[i][neighbor_j] * gaussian1Dkernel_[row];
}
}
tmpMatrix[i].push_back(approxVal);
}
}
// in y direction
for(unsigned j = 0; j < xn_; ++j) {
for(unsigned i = 0; i < yn_; ++i) {
double approxVal = 0.0;
for(unsigned col = 0; col < kernelSize; ++col) {
unsigned neighbor_i = i + col - kernelRad_;
if(neighbor_i >= 0 && neighbor_i < yn_) {
approxVal += tmpMatrix[neighbor_i][j] * gaussian1Dkernel_[col];
}
}
initialMatrix_[i][j] = approxVal;
}
}
}
即,我对边界元素使用相同的内核。
我已经在 100x100 矩阵和具有 2 个半径的内核上测试了这段代码。
而且,例如,我在 1,97 和 2,97 处的元素之间存在很大差异,尽管在该位置的初始矩阵中没有急剧过渡。
也许我需要在计算边界元素的近似值时更改内核?
提前致谢。
我解决了这个问题如下:
不要在 createGaussianKerenel_1D() 函数中规范化内核。
相反,在 getBlurredThermalMap() 函数中执行,如下所示:
void getBlurredThermalMap() {
assert(!gaussian1Dkernel_.empty());
vector<vector<double> > tmpMatrix(yn_);
unsigned kernelSize = 2 * kernelRad_ + 1;
// in x direction
for(unsigned i = 0; i < yn_; ++i) {
for(unsigned j = 0; j < xn_; ++j) {
double approxVal = 0.0;
double sumNorm = 0.0;
for(unsigned row = 0; row < kernelSize; ++row) {
unsigned neighbor_j = j + row - kernelRad_;
// ignore values that are out of bound
if(neighbor_j >= 0 && neighbor_j < xn_) {
approxVal += initialMatrix_[i][neighbor_j] * gaussian1Dkernel_[row];
sumNorm += gaussian1Dkernel_[row];
}
}
approxVal /= sumNorm;
tmpMatrix[i].push_back(approxVal);
}
}
// in y direction
for(unsigned j = 0; j < xn_; ++j) {
for(unsigned i = 0; i < yn_; ++i) {
double approxVal = 0.0;
double sumNorm = 0.0;
for(unsigned col = 0; col < kernelSize; ++col) {
unsigned neighbor_i = i + col - kernelRad_;
if(neighbor_i >= 0 && neighbor_i < yn_) {
approxVal += tmpMatrix[neighbor_i][j] * gaussian1Dkernel_[col];
sumNorm += gaussian1Dkernel_[row];
}
}
approxVal /= sumNorm;
initialMatrix_[i][j] = approxVal;
}
}
}
那可能是因为你没有正确处理边界条件。在你的测试中:
if(neighbor_i >= 0 && neighbor_i < yn_)
第一部分将始终为真,因为 neighbor_i
是一个 unsigned
,因此始终为正数。您可能希望将其更改为带符号的值,稍微修改其声明。您的编译器可以使用适当的警告标志为您检查这些类型的错误(尝试 -Wall -Wextra
)。
编辑: 实际上,测试可能不是您问题的原因,因为您使用的是相对较小的图像,使值 neighbor_i
尝试在其中存储负值时大于 yn_
。
此外,请使用库进行卷积。特别是高斯模糊有非常好的和有效的近似值(Canny-Deriche,傅立叶域中的乘积,......)。
我正在尝试使用高斯二维卷积来模糊矩阵。 但是我在边界元素处得到了急剧的转变。
这是我的一段代码 运行:
// create 1D Kernel
void createGaussianKerenel_1D() {
unsigned kernelSize = 2 * kernelRad_ + 1;
gaussian1Dkernel_ = vector<double>(kernelSize);
double sigma = (double)kernelRad_;
double sum = 0.0;
for(unsigned i = 0; i < kernelSize; ++i) {
gaussian1Dkernel_[i] = gaussian(i, sigma);
sum += gaussian1Dkernel_[i];
}
// normalize
for(unsigned i = 0; i < kernelSize; ++i) {
gaussian1Dkernel_[i] /= sum;
cout << gaussian1Dkernel_[i] << endl;
}
}
// gaussian function
double gaussian(unsigned int i, double sigma) const {
double x = ((double)i - (double)kernelRad_) / sigma;
return exp(-x * x / 2);
}
// do Separable 2D Convolution (in place)
// my initialMatrix_ is of yn_ x xn_ size
void getBlurredThermalMap() {
assert(!gaussian1Dkernel_.empty());
vector<vector<double> > tmpMatrix(yn_);
unsigned kernelSize = 2 * kernelRad_ + 1;
// in x direction
for(unsigned i = 0; i < yn_; ++i) {
for(unsigned j = 0; j < xn_; ++j) {
double approxVal = 0.0;
for(unsigned row = 0; row < kernelSize; ++row) {
unsigned neighbor_j = j + row - kernelRad_;
// ignore values that are out of bound
if(neighbor_j >= 0 && neighbor_j < xn_) {
approxVal += initialMatrix_[i][neighbor_j] * gaussian1Dkernel_[row];
}
}
tmpMatrix[i].push_back(approxVal);
}
}
// in y direction
for(unsigned j = 0; j < xn_; ++j) {
for(unsigned i = 0; i < yn_; ++i) {
double approxVal = 0.0;
for(unsigned col = 0; col < kernelSize; ++col) {
unsigned neighbor_i = i + col - kernelRad_;
if(neighbor_i >= 0 && neighbor_i < yn_) {
approxVal += tmpMatrix[neighbor_i][j] * gaussian1Dkernel_[col];
}
}
initialMatrix_[i][j] = approxVal;
}
}
}
即,我对边界元素使用相同的内核。 我已经在 100x100 矩阵和具有 2 个半径的内核上测试了这段代码。 而且,例如,我在 1,97 和 2,97 处的元素之间存在很大差异,尽管在该位置的初始矩阵中没有急剧过渡。
也许我需要在计算边界元素的近似值时更改内核?
提前致谢。
我解决了这个问题如下:
不要在 createGaussianKerenel_1D() 函数中规范化内核。 相反,在 getBlurredThermalMap() 函数中执行,如下所示:
void getBlurredThermalMap() {
assert(!gaussian1Dkernel_.empty());
vector<vector<double> > tmpMatrix(yn_);
unsigned kernelSize = 2 * kernelRad_ + 1;
// in x direction
for(unsigned i = 0; i < yn_; ++i) {
for(unsigned j = 0; j < xn_; ++j) {
double approxVal = 0.0;
double sumNorm = 0.0;
for(unsigned row = 0; row < kernelSize; ++row) {
unsigned neighbor_j = j + row - kernelRad_;
// ignore values that are out of bound
if(neighbor_j >= 0 && neighbor_j < xn_) {
approxVal += initialMatrix_[i][neighbor_j] * gaussian1Dkernel_[row];
sumNorm += gaussian1Dkernel_[row];
}
}
approxVal /= sumNorm;
tmpMatrix[i].push_back(approxVal);
}
}
// in y direction
for(unsigned j = 0; j < xn_; ++j) {
for(unsigned i = 0; i < yn_; ++i) {
double approxVal = 0.0;
double sumNorm = 0.0;
for(unsigned col = 0; col < kernelSize; ++col) {
unsigned neighbor_i = i + col - kernelRad_;
if(neighbor_i >= 0 && neighbor_i < yn_) {
approxVal += tmpMatrix[neighbor_i][j] * gaussian1Dkernel_[col];
sumNorm += gaussian1Dkernel_[row];
}
}
approxVal /= sumNorm;
initialMatrix_[i][j] = approxVal;
}
}
}
那可能是因为你没有正确处理边界条件。在你的测试中:
if(neighbor_i >= 0 && neighbor_i < yn_)
第一部分将始终为真,因为 neighbor_i
是一个 unsigned
,因此始终为正数。您可能希望将其更改为带符号的值,稍微修改其声明。您的编译器可以使用适当的警告标志为您检查这些类型的错误(尝试 -Wall -Wextra
)。
编辑: 实际上,测试可能不是您问题的原因,因为您使用的是相对较小的图像,使值 neighbor_i
尝试在其中存储负值时大于 yn_
。
此外,请使用库进行卷积。特别是高斯模糊有非常好的和有效的近似值(Canny-Deriche,傅立叶域中的乘积,......)。