1 高斯滤波
原理就不讲了,文章很多。
基础代码请参阅:
C#图像处理OpenCV开发指南(CVStar,03)——基于.NET 6的图像处理桌面程序开发实践第一步https://blog.csdn.net/beijinghorn/article/details/134684471?spm=1001.2014.3001.5502
1.1 函数原型
void GaussianBlur(src, dst, ksize, sigmaX, sigmaY, borderType)
1.2 参数说明
- src 是需要处理的图像,即原始图像。它能够有任意数量的通道,并能对各个通道 独立处理。图像深度应该是CV_8U、CV_16U、CV_16S、CV_32F 或者 CV_64F中的一 种。
- dst 是返回值,表示进行高斯滤波后得到的处理结果。
- ksize 是滤波核的大小。滤波核大小是指在滤波处理过程中其邻域图像的高度和宽 度。需要注意,滤波核的值必须是奇数。
- sigmaX 是卷积核在水平方向上(X 轴方向)的标准差,其控制的是权重比例。
- sigmaY 是卷积核在垂直方向上(Y轴方向)的标准差。如果将该值设置为0,则只采用sigmaX的值
如果sigmaX和sigmaY都是0,则通过ksize.width和ksize.height计算得到。其中:
sigmaX=0.3 × [(ksize.width-1)×0.5-1] + 0.8
sigmaY=0.3 × [(ksize.height-1)×0.5-1] + 0.8 - borderType是边界样式,该值决定了以何种方式处理边界。一般情况下,不需要考虑该值,直接采用默认值即可。
在该函数中,sigmaY和borderType是可选参数。
sigmaX是必选参数,但是可以将该参数设置为0,让函数自己去计算sigmaX的具体值。
1.3 C++例子程序
#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
int DELAY_CAPTION = 1500;
int DELAY_BLUR = 100;
int MAX_KERNEL_LENGTH = 31;
Mat src; Mat dst;
char window_name[] = "Smoothing Demo";
int display_caption( const char* caption );
int display_dst( int delay );
int main( int argc, char ** argv )
{
namedWindow( window_name, WINDOW_AUTOSIZE );
const char* filename = argc >=2 ? argv[1] : "lena.jpg";
src = imread( samples::findFile( filename ), IMREAD_COLOR );
if (src.empty())
{
printf(" Error opening image\n");
printf(" Usage:\n %s [image_name-- default lena.jpg] \n", argv[0]);
return EXIT_FAILURE;
}
if( display_caption( "Original Image" ) != 0 )
{
return 0;
}
dst = src.clone();
if( display_dst( DELAY_CAPTION ) != 0 )
{
return 0;
}
if( display_caption( "Homogeneous Blur" ) != 0 )
{
return 0;
}
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{
blur( src, dst, Size( i, i ), Point(-1,-1) );
if( display_dst( DELAY_BLUR ) != 0 )
{
return 0;
}
}
if( display_caption( "Gaussian Blur" ) != 0 )
{
return 0;
}
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{
GaussianBlur( src, dst, Size( i, i ), 0, 0 );
if( display_dst( DELAY_BLUR ) != 0 )
{
return 0;
}
}
if( display_caption( "Median Blur" ) != 0 )
{
return 0;
}
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{
medianBlur ( src, dst, i );
if( display_dst( DELAY_BLUR ) != 0 )
{
return 0;
}
}
if( display_caption( "Bilateral Blur" ) != 0 )
{
return 0;
}
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
{
bilateralFilter ( src, dst, i, i*2, i/2 );
if( display_dst( DELAY_BLUR ) != 0 )
{
return 0;
}
}
display_caption( "Done!" );
return 0;
}
int display_caption( const char* caption )
{
dst = Mat::zeros( src.size(), src.type() );
putText( dst, caption,
Point( src.cols/4, src.rows/2),
FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255) );
return display_dst(DELAY_CAPTION);
}
int display_dst( int delay )
{
imshow( window_name, dst );
int c = waitKey ( delay );
if( c >= 0 ) { return -1; }
return 0;
}
2 高斯滤波的代码
2.1 参数输入的界面相关代码
if (abKSize == null) abKSize = new Label();
abKSize.Parent = panelTop;
abKSize.Left = btnFunction.Left;
abKSize.Top = btnFunction.Top + btnFunction.Height + 5;
abKSize.Text = "Radium: ";
if (txtKSize == null) txtKSize = new TextBox();
txtKSize.Parent = panelTop;
txtKSize.Left = abKSize.Left + abKSize.Width + 5;
txtKSize.Top = abKSize.Top;
txtKSize.Text = "15";
if (abSigmaX == null) abSigmaX = new Label();
abSigmaX.Parent = panelTop;
abSigmaX.Left = txtKSize.Left + txtKSize.Width + 5;
abSigmaX.Top = btnFunction.Top + btnFunction.Height + 5;
abSigmaX.Text = "SigmaX: ";
if (txtSigmaX == null) txtSigmaX = new TextBox();
txtSigmaX.Parent = panelTop;
txtSigmaX.Left = abSigmaX.Left + abSigmaX.Width + 5;
txtSigmaX.Top = abKSize.Top;
txtSigmaX.Text = "0.0";
if (abSigmaY == null) abSigmaY = new Label();
abSigmaY.Parent = panelTop;
abSigmaY.Left = txtSigmaX.Left + txtSigmaX.Width + 5;
abSigmaY.Top = btnFunction.Top + btnFunction.Height + 5;
abSigmaY.Text = "SigmaX: ";
if (txtSigmaY == null) txtSigmaY = new TextBox();
txtSigmaY.Parent = panelTop;
txtSigmaY.Left = abSigmaY.Left + abSigmaY.Width + 5;
txtSigmaY.Top = abKSize.Top;
txtSigmaY.Text = "0.0";
2.2 核心代码
private void GaussianBlur(object? sender, EventArgs? e)
{
if (txtKSize.Text.Trim().Length < 1) { MessageBox.Show("rad Required!"); return; }
if (!int.TryParse(txtKSize.Text.Trim(), out int ksize)) { MessageBox.Show("Invalid rad number!"); return; }
if (ksize < 1 || ksize > 100) { MessageBox.Show("Invalid rad number!"); return; }
if ((ksize % 2) != 1) { MessageBox.Show("Odd number required for rad!"); return; }
if (txtSigmaX.Text.Trim().Length < 1) { MessageBox.Show("SigmaX Required!"); return; }
if (!double.TryParse(txtSigmaX.Text.Trim(), out double sigmax)) { MessageBox.Show("Invalid SigmaX!"); return; }
if (sigmax < -1.0 || sigmax > 1.0) { MessageBox.Show("Invalid sigmaX(-1.0---1.0)!"); return; }
if (txtSigmaY.Text.Trim().Length < 1) { MessageBox.Show("SigmaY Required!"); return; }
if (!double.TryParse(txtSigmaY.Text.Trim(), out double sigmay)) { MessageBox.Show("Invalid SigmaY!"); return; }
if (sigmay < -1.0 || sigmay > 1.0) { MessageBox.Show("Invalid sigmaY(-1.0---1.0)!"); return; }
Mat src = Cv2.ImRead(sourceImage);
Mat dst = new Mat();
Cv2.GaussianBlur(
src: src,
dst: dst,
ksize: new OpenCvSharp.Size(ksize, ksize),
sigmaX: sigmax,
sigmaY: sigmay,
borderType: BorderTypes.Default);
picResult.Image = CVUtility.Mat2Bitmap(dst);
PicAutosize(picResult);
}
3 完整的 Form1.cs 代码
using OpenCvSharp;
#pragma warning disable CS8602
namespace Legal.Truffer.CVStar
{
public partial class Form1 : Form
{
string[] ImgExtentions = {
"*.*|*.*",
"JPEG|*.jpg;*.jpeg",
"GIF|*.gif",
"PNG|*.png",
"TIF|*.tif;*.tiff",
"BMP|*.bmp"
};
private int original_width { get; set; } = 0;
private int original_height { get; set; } = 0;
private string sourceImage { get; set; } = "";
Panel? panelTop { get; set; } = null;
Panel? panelBotton { get; set; } = null;
PictureBox? picSource { get; set; } = null;
PictureBox? picResult { get; set; } = null;
Button? btnLoad { get; set; } = null;
Button? btnSave { get; set; } = null;
Button? btnFunction { get; set; } = null;
Label? abKSize { get; set; } = null;
TextBox? txtKSize { get; set; } = null;
Label? abSigmaX { get; set; } = null;
TextBox? txtSigmaX { get; set; } = null;
Label? abSigmaY { get; set; } = null;
TextBox? txtSigmaY { get; set; } = null;
public Form1()
{
InitializeComponent();
this.Text = "OPENCV C#编程入手教程 POWERED BY 深度混淆(CSDN.NET)";
this.StartPosition = FormStartPosition.CenterScreen;
GUI();
this.Resize += FormResize;
}
private void FormResize(object? sender, EventArgs? e)
{
if (this.Width < 200) { this.Width = 320; return; }
if (this.Height < 200) { this.Height = 320; return; }
GUI();
}
private void GUI()
{
if (panelTop == null) panelTop = new Panel();
panelTop.Parent = this;
panelTop.Top = 5;
panelTop.Left = 5;
panelTop.Width = this.Width - 26;
panelTop.Height = 85;
panelTop.BorderStyle = BorderStyle.FixedSingle;
panelTop.BackColor = Color.FromArgb(200, 200, 255);
if (panelBotton == null) panelBotton = new Panel();
panelBotton.Parent = this;
panelBotton.Top = panelTop.Top + panelTop.Height + 3;
panelBotton.Left = 5;
panelBotton.Width = panelTop.Width;
panelBotton.Height = this.Height - panelBotton.Top - 55;
panelBotton.BorderStyle = BorderStyle.FixedSingle;
if (picSource == null) picSource = new PictureBox();
picSource.Parent = panelBotton;
picSource.Left = 5;
picSource.Top = 5;
picSource.Width = (panelBotton.Width - 10) / 2;
picSource.Height = (panelBotton.Height - 10);
picSource.BorderStyle = BorderStyle.FixedSingle;
if (picResult == null) picResult = new PictureBox();
picResult.Parent = panelBotton;
picResult.Left = picSource.Left + picSource.Width + 5;
picResult.Top = picSource.Top;
picResult.Width = picSource.Width;
picResult.Height = picSource.Height;
picResult.BorderStyle = BorderStyle.FixedSingle;
original_width = picSource.Width;
original_height = picSource.Height;
if (btnLoad == null) btnLoad = new Button();
btnLoad.Parent = panelTop;
btnLoad.Left = 5;
btnLoad.Top = 5;
btnLoad.Width = 90;
btnLoad.Height = 38;
btnLoad.Cursor = Cursors.Hand;
btnLoad.Text = "Load";
btnLoad.Click += Load_Image;
btnLoad.BackColor = Color.LightCoral;
if (btnSave == null) btnSave = new Button();
btnSave.Parent = panelTop;
btnSave.Left = panelTop.Width - btnSave.Width - 25;
btnSave.Top = btnLoad.Top;
btnSave.Width = 90;
btnSave.Height = 38;
btnSave.Cursor = Cursors.Hand;
btnSave.Text = "Save";
btnSave.Click += Save;
btnSave.BackColor = Color.LightCoral;
if (btnFunction == null) btnFunction = new Button();
btnFunction.Parent = panelTop;
btnFunction.Left = btnLoad.Left + btnLoad.Width + 5;
btnFunction.Top = btnLoad.Top;
btnFunction.Width = 90;
btnFunction.Height = 38;
btnFunction.Cursor = Cursors.Hand;
btnFunction.Text = "GaussianBlur";
btnFunction.Click += GaussianBlur;
btnFunction.BackColor = Color.LightCoral;
if (abKSize == null) abKSize = new Label();
abKSize.Parent = panelTop;
abKSize.Left = btnFunction.Left;
abKSize.Top = btnFunction.Top + btnFunction.Height + 5;
abKSize.Text = "Radium: ";
if (txtKSize == null) txtKSize = new TextBox();
txtKSize.Parent = panelTop;
txtKSize.Left = abKSize.Left + abKSize.Width + 5;
txtKSize.Top = abKSize.Top;
txtKSize.Text = "15";
if (abSigmaX == null) abSigmaX = new Label();
abSigmaX.Parent = panelTop;
abSigmaX.Left = txtKSize.Left + txtKSize.Width + 5;
abSigmaX.Top = btnFunction.Top + btnFunction.Height + 5;
abSigmaX.Text = "SigmaX: ";
if (txtSigmaX == null) txtSigmaX = new TextBox();
txtSigmaX.Parent = panelTop;
txtSigmaX.Left = abSigmaX.Left + abSigmaX.Width + 5;
txtSigmaX.Top = abKSize.Top;
txtSigmaX.Text = "0.0";
if (abSigmaY == null) abSigmaY = new Label();
abSigmaY.Parent = panelTop;
abSigmaY.Left = txtSigmaX.Left + txtSigmaX.Width + 5;
abSigmaY.Top = btnFunction.Top + btnFunction.Height + 5;
abSigmaY.Text = "SigmaX: ";
if (txtSigmaY == null) txtSigmaY = new TextBox();
txtSigmaY.Parent = panelTop;
txtSigmaY.Left = abSigmaY.Left + abSigmaY.Width + 5;
txtSigmaY.Top = abKSize.Top;
txtSigmaY.Text = "0.0";
PicAutosize(picSource);
PicAutosize(picResult);
}
private void Load_Image(object? sender, EventArgs? e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = String.Join("|", ImgExtentions);
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
sourceImage = openFileDialog.FileName;
picSource.Image = Image.FromFile(sourceImage);
picResult.Image = picSource.Image;
PicAutosize(picSource);
PicAutosize(picResult);
}
}
private void PicAutosize(PictureBox pb)
{
if (pb == null) return;
if (pb.Image == null) return;
Image img = pb.Image;
int w = original_width;
int h = w * img.Height / img.Width;
if (h > original_height)
{
h = original_height;
w = h * img.Width / img.Height;
}
pb.SizeMode = PictureBoxSizeMode.Zoom;
pb.Width = w;
pb.Height = h;
pb.Image = img;
pb.Refresh();
}
private void Save(object? sender, EventArgs? e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = String.Join("|", ImgExtentions);
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
picResult.Image.Save(saveFileDialog.FileName);
MessageBox.Show("Image Save to " + saveFileDialog.FileName);
}
}
private void GaussianBlur(object? sender, EventArgs? e)
{
if (txtKSize.Text.Trim().Length < 1) { MessageBox.Show("rad Required!"); return; }
if (!int.TryParse(txtKSize.Text.Trim(), out int ksize)) { MessageBox.Show("Invalid rad number!"); return; }
if (ksize < 1 || ksize > 100) { MessageBox.Show("Invalid rad number!"); return; }
if ((ksize % 2) != 1) { MessageBox.Show("Odd number required for rad!"); return; }
if (txtSigmaX.Text.Trim().Length < 1) { MessageBox.Show("SigmaX Required!"); return; }
if (!double.TryParse(txtSigmaX.Text.Trim(), out double sigmax)) { MessageBox.Show("Invalid SigmaX!"); return; }
if (sigmax < -1.0 || sigmax > 1.0) { MessageBox.Show("Invalid sigmaX(-1.0---1.0)!"); return; }
if (txtSigmaY.Text.Trim().Length < 1) { MessageBox.Show("SigmaY Required!"); return; }
if (!double.TryParse(txtSigmaY.Text.Trim(), out double sigmay)) { MessageBox.Show("Invalid SigmaY!"); return; }
if (sigmay < -1.0 || sigmay > 1.0) { MessageBox.Show("Invalid sigmaY(-1.0---1.0)!"); return; }
Mat src = Cv2.ImRead(sourceImage);
Mat dst = new Mat();
Cv2.GaussianBlur(
src: src,
dst: dst,
ksize: new OpenCvSharp.Size(ksize, ksize),
sigmaX: sigmax,
sigmaY: sigmay,
borderType: BorderTypes.Default);
picResult.Image = CVUtility.Mat2Bitmap(dst);
PicAutosize(picResult);
}
}
}
4 运行效果
通过修改 Radium SigmaX SigmaY 等参数可加深理解。