首页 > 编程语言 >基于C#语言的图片处理应用程序

基于C#语言的图片处理应用程序

时间:2022-10-16 21:58:57浏览次数:57  
标签:基于 Dist C# Button 应用程序 pictureBox Undo bitmap Mosaic

基于C#语言的图片处理应用程序

1. 任务

  编写一个既能为图片打码(添加马赛克)又能为图片添加晕影特效的图片处理应用程序。

2. 窗体设计

  设计步骤:根据要实现的功能,设计如图1所示的用户界面,其具体设置如表1所示。

图1 最终用户界面

表1 主要控件的基本属性列表

控件类型

控件名称

触发事件

按钮
Button

Button_Open

Button_Open_Click

Button_Save

Button_Save_Click

Button_Mosaic

Button_Mosaic_Click

Button_Vignetting

Button_Vignetting_Click

Button_Undo

Button_Undo_Click

Button_Reload

Button_Reload_Click

滑块
trackBar

trackBar1

trackBar1_Scroll

trackBar2

trackBar2_Scroll

trackBar3

trackBar3_Scroll

文本输入框
TextBox

Bar1_Num

Bar1_Num_TextChanged

Bar2_Num

Bar2_Num_TextChanged

Bar3_Num

Bar3_Num_TextChanged

标签
Label

  

label10

label10_Click

图片框
pictureBox

pictureBox

pictureBox_MouseDown

pictureBox_MouseUp

pictureBox_MouseMove

 

3. 程序设计

(1)打开图片:

①模块功能:

  该模块主要要实现的功能是打开图片,并把图片里的信息写入bitmap中,如图2所示。

图2 打开图片

②实现方法:

  该模块主要是通过OpenFileDialag这个类来实现的,并且通过Application.StartupPath使打开的地址始终为应用程序所在的地址。

③实现程序:

 1 //----------------------打开图片----------------------//
 2 private void Button_Open_Click(object sender, EventArgs e)
 3 {
 4     label10.Text = "请选择图片";
 5     using (OpenFileDialog openFileDialog = new OpenFileDialog())
 6     {
 7         string OpenFile_DIR = Application.StartupPath;
 8         openFileDialog.InitialDirectory = OpenFile_DIR;
 9         openFileDialog.Filter = "jpg files (*.jpg)|*.jpg|All files (*.*)|*.*";
10         openFileDialog.FilterIndex = 1;
11         openFileDialog.RestoreDirectory = true;
12         if (openFileDialog.ShowDialog() == DialogResult.OK)
13         {
14             string path = openFileDialog.FileName;
15             bitmap = (Bitmap)Image.FromFile(path);
16             pictureBox.Image = bitmap.Clone() as Image;
17             bitmap_Mosaic = bitmap.Clone() as Bitmap;
18             label10.Text = string.Format($"图片大小\r\n{pictureBox.Image.Width}x{pictureBox.Image.Height}");
19         }
20         else
21             label10.Text = "图片大小\r\n ";
22     }
23 }
  (2)保存图片: ①模块功能:

  该模块的主要功能是把处理后的pictureBox内的图片导出到电脑文件中,并用jpg,bmp或gif格式将其储存起来,如图3所示。

图3 保存图片

②实现方法:   该模块主要是通过SaveFileDialog这个类来实现的。 ③实现程序:
 1 //----------------------保存图片----------------------//  
 2 private void Button_Save_Click(object sender, EventArgs e)  
 3 {  
 4     bool isSave = true;  
 5     SaveFileDialog saveFileDialog1 = new SaveFileDialog();  
 6     if (saveFileDialog1.ShowDialog() == DialogResult.OK)  
 7     {  
 8         string fileName = saveFileDialog1.FileName.ToString();  
 9         if (fileName != "" && fileName != null)  
10         {  
11             string fileExtName = fileName.Substring(fileName.LastIndexOf(".") + 1).ToString();  
12             System.Drawing.Imaging.ImageFormat imgformat = null;  
13             if (fileExtName != "")  
14             {  
15                 switch (fileExtName)  
16                 {  
17                     case "jpg":  
18                         imgformat = System.Drawing.Imaging.ImageFormat.Jpeg;  
19                         break;  
20                     case "bmp":  
21                         imgformat = System.Drawing.Imaging.ImageFormat.Bmp;  
22                         break;  
23                     case "gif":  
24                         imgformat = System.Drawing.Imaging.ImageFormat.Gif;  
25                         break;  
26                     default:  
27                         MessageBox.Show("格式错误 只能存取为: jpg,bmp,gif 格式");  
28                         isSave = false;  
29                         break;  
30                 }  
31             }   
32             if (imgformat == null)//默认保存为JPG格式  
33             {  
34                 imgformat = System.Drawing.Imaging.ImageFormat.Jpeg;  
35             }  
36             if (isSave)  
37             {  
38                 try  
39                 {  
40                     this.pictureBox.Image.Save(fileName, imgformat);  
41                     //MessageBox.Show("图片已经成功保存!");  
42                 }  
43                 catch  
44                 {  
45                     MessageBox.Show("保存失败,你还没有打开图片");  
46                 }  
47             }  
48         }  
49     }  
50 }  

 

(3)添加格子: ①模块功能:

  该模块要实现的功能是通过在pictureBox内点击并拖动鼠标来为pictureBox里的图片添加马赛克,同时用户可以通过滑动滑块trackBar1或更改文本输入框Bar1_Num的值来改变所要添加马赛克的大小,如图4所示。

图4 添加格子

②实现方法:

  首先用户单击Mosaic按钮会使按钮进入不可用状态,然后再通过判断Mosaic按钮是否为不可用状态程序即可知道用户当前所选择的模式是否为"格子模式"。

  其次只要用户在pictureBox中按下鼠标,程序便会给mouse_down标志位置1,直至用户在pictureBox中松开鼠标,该标志位才会清0,而在标志位为1的过程中,只要用户在pictureBox中移动鼠标便会重复执行Add_Mosaic函数。

  而Add_Mosaic函数首先需要做的是读取到当前鼠标所在位置的像素点坐标。而因为图片在pictureBox中是通过等比例缩放Zoom的方式打开的,所以鼠标当前所在的位置与像素点的坐标并不完全能对应上,因此在这里需要通过数学方法对此进行转换。如图4所示,具体的数学表达式如下:

    $\begin{cases} y_p=y_m\times \frac{h_p}{h_m}\\ x_p=\left( x_m-\frac{w_m-\frac{h_m}{h_p}w_p}{2} \right) \times \frac{h_p}{h_m}\\\end{cases}$

  其中$\left( x_p, y_p \right) $为像素点坐标,$\left( x_m, y_m \right) $为鼠标位置,$w_p\times h_p$为图片的大小,$w_m\times h_m$为pictureBox的大小。

  之后再根据用户所设定的格子大小,对鼠标所在位置周围的一定像素点进行取均值处理,即可实现该模块的功能。

③实现程序:

 1 //----------------------格子模式----------------------//  
 2 private void Button_Mosaic_Click(object sender, EventArgs e)  
 3 {  
 4     if (bitmap != null)  
 5     {  
 6         Button_Mosaic.Enabled = false;  
 7         Button_Vignetting.Enabled = true;  
 8     }  
 9     else  
10         MessageBox.Show("请先打开图片", "提示");  
11 }  
12     
13 //----------------------添加格子----------------------//  
14 private void Add_Mosaic(object sender, EventArgs e)  
15 {  
16     if (bitmap != null)  
17     {  
18         bitmap_Mosaic = pictureBox.Image as Bitmap;  
19         Stopwatch sw = new Stopwatch();  
20         sw.Reset();  
21         sw.Restart();  
22     
23         double width = bitmap_Mosaic.Width;  
24         double height = bitmap_Mosaic.Height;  
25         double k, Dist_Side;  
26         double centre_x, centre_y;  
27         int red = 0, green = 0, blue = 0;//16777216=4096*4096  
28         Color pixel;  
29     
30         if (width / height < 16f / 9f)  
31         {  
32             k = 657 / height;//缩放系数  
33             Dist_Side = (1168 - k * width) / 2;//边框距离  
34             centre_x = (mouse_x - Dist_Side) / k;  
35             centre_y = mouse_y / k;  
36         }  
37         else  
38         {  
39             k = 1168 / width;//缩放系数  
40             Dist_Side = (657 - k * height) / 2;//边框距离  
41             centre_x = mouse_x / k;  
42             centre_y = (mouse_y - Dist_Side) / k;  
43         }  
44     
45         int x = (int)Math.Round(centre_x);  
46         int y = (int)Math.Round(centre_y);  
47         double size_Mosaic = width < height ? width / 10 : height / 10;  
48         size_Mosaic = size_Mosaic * trackBar1.Value / trackBar1.Maximum;//格子大小  
49         int count = 0;  
50     
51         for (int i = x - (int)size_Mosaic; i <= x + (int)size_Mosaic; i++)  
52         {  
53             if (i < 0) i = 0;  
54             if (i > width - 1) break;  
55             for (int j = y - (int)size_Mosaic; j <= y + (int)size_Mosaic; j++)  
56             {  
57                 if (j < 0) j = 0;  
58                 if (j > height - 1) break;  
59                 if ((i - x) * (i - x) + (j - y) * (j - y) <= size_Mosaic * size_Mosaic)  
60                 {  
61                     pixel = bitmap_Mosaic.GetPixel(i, j);  
62                     red += pixel.R;  
63                     green += pixel.G;  
64                     blue += pixel.B;  
65                     count++;  
66                 }  
67             }  
68         }  
69         if (count != 0)  
70         {  
71             red /= count;  
72             green /= count;  
73             blue /= count;  
74         }  
75     
76         for (int i = x - (int)size_Mosaic; i <= x + (int)size_Mosaic; i++)  
77         {  
78             if (i < 0) i = 0;  
79             if (i > width - 1) break;  
80             for (int j = y - (int)size_Mosaic; j <= y + (int)size_Mosaic; j++)  
81             {  
82                 if (j < 0) j = 0;  
83                 if (j > height - 1) break;  
84                 if ((i - x) * (i - x) + (j - y) * (j - y) <= size_Mosaic * size_Mosaic)  
85                 {  
86                     try//为什么总是报错  
87                     {  
88                         bitmap_Mosaic.SetPixel(i, j, Color.FromArgb(red, green, blue));  
89                     }  
90                     catch { }  
91                 }  
92             }  
93         }  
94         sw.Stop();  
95         tConsuming = sw.ElapsedMilliseconds;  
96         label10.Text = string.Format($"处理时间\r\n{tConsuming / 1000}.{tConsuming % 1000 / 100}{tConsuming % 100 / 10}{tConsuming % 10}s");  
97         pictureBox.Image = bitmap_Mosaic.Clone() as Image;  
98     }  
99 }  

 

(4)添加晕影: ①模块功能:

  该功能所要实现的功能是为pictureBox里的图片添加晕影效果,同时用户可以通过滑块或文本输入框来更改光圈的大小和羽化的程度,此外用户还可以通过鼠标点击图片来改变所要添加晕影的中心,如图5所示。

图5 添加晕影

②实现方法:

  首先用户单击Vignetting按钮会使按钮进入不可用状态,然后再通过判断Vignetting按钮是否为不可用状态程序即可知道用户当前所选择的模式是否为"晕影模式"。

  其次只要用户在pictureBox中按下鼠标,程序便会执行Add_Vignetting函数。而Add_Mosaic函数首先需要做的是读取到当前鼠标所在位置的像素点坐标,方法同"添加格子"模块中所描述的一样。之后再根据用户所设定的光圈大小及羽化程度给图片中的每个像素乘上一个调整因数后即可得到最终的效果。

  如图6所示,光圈大小其实控制的是图中小圆的大小,取值范围为[0,图片对角距离/2],而羽化程度控制的则是图中大圆的大小,取值范围为[小圆大小,+∞),同时小圆与大圆之间的灰度变化是线性的。根据以上要求并经过反复的试验可推导出以下公式:

    $\begin{cases} r_0=\sqrt{\left( x_i-x_c \right) ^2+\left( y_j-y_c \right) ^2}\\ r_1=D_{\max}\times n_1\\ r_2=\frac{r_1}{1-n_2}\\ f\mathrm{ac}tor=\frac{r_2-r_0}{r_2-r_1}\\\end{cases}$

  其中为像素点与中心点的距离,为小圆的半径,为图片对角距离/2,为文本框Bar2_Num的值(即光圈大小),为大圆的半径,为文本框Bar3_Num的值(即羽化程度),为最终的调整因数。

图6 晕影原理图示

③实现程序:

 1 //----------------------晕影模式----------------------//  
 2 private void Button_Vignetting_Click(object sender, EventArgs e)  
 3 {  
 4     if (bitmap != null)  
 5     {  
 6         mouse_x = 584;  
 7         mouse_y = 328;  
 8         Button_Vignetting.Enabled = false;  
 9         Button_Mosaic.Enabled = true;  
10         Add_Vignetting(sender, e);  
11     }  
12     else  
13         MessageBox.Show("请先打开图片", "提示");  
14 }  
15     
16 //----------------------添加晕影----------------------//  
17 private void Add_Vignetting(object sender, EventArgs e)  
18 {  
19     if (bitmap != null)  
20     {  
21         label10.Text = "请稍候...";  
22         Application.DoEvents();  
23         index_Undo++;  
24         bitmap_Undo.Add(pictureBox.Image.Clone() as Bitmap);  
25         Undo_Assist.Add(0);  
26         bitmap_Vignetting = bitmap_Mosaic.Clone() as Bitmap;  
27         Stopwatch sw = new Stopwatch();  
28         sw.Reset();  
29         sw.Restart();  
30         double width = bitmap_Vignetting.Width;  
31         double height = bitmap_Vignetting.Height;  
32         double k, Dist_Side;  
33         double centre_x, centre_y;  
34         double MaxDist = Math.Sqrt(Math.Pow(width / 2, 2) + Math.Pow(height / 2, 2));  
35         double CurrDist = 0;  
36         double Dist_Dark, Dist_Darkest, factor;  
37         int red, green, blue;  
38         Color pixel;  
39     
40         if (width / height < 16f / 9f)  
41         {  
42             k = 657 / height;//缩放系数  
43             Dist_Side = (1168 - k * width) / 2;//边框距离  
44             centre_x = (mouse_x - Dist_Side) / k;  
45             centre_y = mouse_y / k;  
46         }  
47         else  
48         {  
49             k = 1168 / width;//缩放系数  
50             Dist_Side = (657 - k * height) / 2;//边框距离  
51             centre_x = mouse_x / k;  
52             centre_y = (mouse_y - Dist_Side) / k;  
53         }  
54     
55         for (int i = 0; i < width; i++)  
56         {  
57             for (int j = 0; j < height; j++)  
58             {  
59                 CurrDist = Math.Sqrt(Math.Pow(i - centre_x, 2) + Math.Pow(j - centre_y, 2));  
60                 Dist_Dark = MaxDist * trackBar2.Value / trackBar2.Maximum;//刚开始变暗的距离  
61                 if (trackBar3.Value == trackBar3.Maximum)  
62                     trackBar3.Value -= 1;  
63                 Dist_Darkest = Dist_Dark / (1 - 1d * trackBar3.Value / trackBar3.Maximum);//最暗的距离  
64                 if (Dist_Darkest == Dist_Dark)  
65                     Dist_Darkest += 1;  
66                 factor = (Dist_Darkest - CurrDist) / (Dist_Darkest - Dist_Dark);//调整因数  
67                 pixel = bitmap_Vignetting.GetPixel(i, j);  
68                 if (CurrDist > Dist_Dark)  
69                 {  
70                     red = (int)(pixel.R * factor);  
71                     green = (int)(pixel.G * factor);  
72                     blue = (int)(pixel.B * factor);  
73                     if (red < 0) red = 0;  
74                     if (green < 0) green = 0;  
75                     if (blue < 0) blue = 0;  
76                     if (red > 255) red = 255;  
77                     if (green > 255) green = 255;  
78                     if (blue > 255) blue = 255;  
79                 }  
80                 else  
81                 {  
82                     red = (int)(pixel.R);  
83                     green = (int)(pixel.G);  
84                     blue = (int)(pixel.B);  
85                 }  
86                 bitmap_Vignetting.SetPixel(i, j, Color.FromArgb(red, green, blue));  
87             }  
88         }  
89     
90         sw.Stop();  
91         tConsuming = sw.ElapsedMilliseconds;  
92         label10.Text = string.Format($"处理时间\r\n{tConsuming / 1000}.{tConsuming % 1000 / 100}{tConsuming % 100 / 10}{tConsuming % 10}s");  
93         pictureBox.Image = bitmap_Vignetting.Clone() as Image;  
94     }  
95 }  

 

(5)回撤多步: ①模块功能:

  该模块实现的功能是通过单击Undo按钮来撤回之前所执行过的相关操作,如图7和图8所示。

图7 回撤多步前

图8 回撤多步后

②实现方法:

  首先在程序的开头用List<Bitmap>来创建一个泛型集合bitmap_Undo,之后在每次操作之前都会先将图片框pictureBox中的图片添加到该泛型集合bitmap_Undo中,同时索引index_Undo加1,而在需要执行回撤时,即会拿出bitmap_Undo[index_Undo]放入图片框pictureBox中,并将其从泛型集合bitmap_Undo中删去,同时索引index_Undo减1。需要特别注意的是,由于晕影模式是以bitmap_Mosaic作为蓝本进行处理的,而非采用图片框中的图片,因此还需添加泛型集合Undo_Assist以便记录下用户对图片进行处理的顺序,以对bitmap_Mosaic进行还原。

③实现程序:

 1 //----------------------回撤一步----------------------//  
 2 private void Button_Undo_Click(object sender, EventArgs e)  
 3 {  
 4     if (index_Undo > -1)   
 5     {  
 6         pictureBox.Image = bitmap_Undo[index_Undo].Clone() as Image;  
 7         if (Undo_Assist[index_Undo] == 1)  
 8             bitmap_Mosaic = bitmap_Undo[index_Undo].Clone() as Bitmap;  
 9         bitmap_Undo.RemoveAt(index_Undo);  
10         Undo_Assist.RemoveAt(index_Undo);  
11         index_Undo--;  
12     }  
13 }
  (6)重新加载: ①模块功能:

  该模块主要是为用户提供一个快速一键复原图片的功能,也就是说用户可以通过单击Reload按钮来重新加载原始导入的图片,如图9所示。

图9 重新加载

②实现方法:

  通过将原始的bitmap替换图片框中的图片和bitmap_Mosaic即可完成对图片的重新加载。

③实现程序:

1 //----------------------重新加载----------------------//  
2 private void Button_Reload_Click(object sender, EventArgs e)  
3 {  
4     pictureBox.Image = bitmap.Clone() as Image;  
5     bitmap_Mosaic = bitmap.Clone() as Bitmap;  
6 }  

 

(7)信息提示: ①模块功能:

  该模块主要是为用户提供相关的信息提示功能,如在图片处理的时候会有"请稍候…"的提示,同时用户可以通过单击该标签来查看图片大小及处理时间等相关的信息,如图10所示。

图10 信息提示

②实现方法:

  通过检测该标签中是否含有"图片大小",若是则更换成处理时间的信息,若否则更换成图片大小的信息即可。

③实现程序:

 1 //----------------------信息提示----------------------//  
 2 private void label10_Click(object sender, EventArgs e)  
 3 {  
 4     if (label10.Text.Contains("图片大小"))  
 5         if (tConsuming == 0)  
 6             label10.Text = "处理时间\r\n ";  
 7         else  
 8             label10.Text = string.Format($"处理时间\r\n{tConsuming / 1000}.{tConsuming % 1000 / 100}{tConsuming % 100 / 10}{tConsuming % 10}s");  
 9     else  
10         try  
11         {  
12             label10.Text = string.Format($"图片大小\r\n{pictureBox.Image.Width}x{pictureBox.Image.Height}");  
13         }  
14         catch  
15         {  
16             label10.Text = "图片大小\r\n ";  
17         }  
18 }  

标签:基于,Dist,C#,Button,应用程序,pictureBox,Undo,bitmap,Mosaic
From: https://www.cnblogs.com/Kelvin-Wu/p/16796966.html

相关文章

  • Kubernetes学习笔记(四十):KodeKloud Mock Exam - 2
    Question1(15')Takeabackupoftheetcdclusterandsaveitto/opt/etcd-backup.db.Question2(15')CreateaPodcalledredis-storagewithimage:redis:alp......
  • C++实现链表反转
    #include"stdio.h"structListNode{intval;ListNode*next;ListNode(intx):val(x),next(NULL){}};classSolution{public:ListNode*Rev......
  • Codeforces Round #748 (Div. 3) E
    E.GardenerandTree显然我们对于每一个叶节点是度数为1的我们如果删除外层叶节点的时候显然也要改变与他与他连接的节点的度数而只有可以能在这些节点里诞生新的叶节......
  • 22 暑期C班 Day7—附加赛
    线段树什么的最讨厌了已经没有什么好害怕的了我才不是萝莉控呢......
  • ArcPy获取要素类的别名
    我们知道,使用ArcMap在文件地理数据库(*.gdb)里要素类或表可设置别名,而个人地理数据库(*.mdb)和ShapeFile文件(*.shp)不可设置。那么在ArcPy对应的也一样,获取图层别名不是在Fea......
  • 22暑期C班
    22暑期CD班联考Day4之降低力度22暑期C班Day7—附加赛22暑期CD班Day8—附加赛3......
  • Prometheus监控携带metrics接口的服务
    目录Prometheus监控携带metrics接口的服务一、prometheus监控携带metrics接口的服务的流程:二、通过prometheus监控ETCD1、测试ETCD服务的metrics接口2、通过EndPrints获取......
  • 【2022 CCPC Henan Provincial Collegiate Programming Contest #K】复合函数
    题目链接K.复合函数输入文件:standardinput输出文件:standardoutput时间限制:1second空间限制:512megabytes给定正整数\(n\),并记\(I_n={1,2,\cdots,n}\)。给定......
  • CocoaPods升级与Ruby升级问题
    使用Homebrew升级Ruby后,替换系统的Ruby参考以下文章:https://mac.install.guide/ruby/13.htmlpod升级问题参考:https://www.it610.com/article/1294593630094303232.htm......
  • 【数据结构】队列的定义以及C语言代码的实现(仅供学习交流使用)
    1、队列的定义队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(FirstInFirstOut)入队列:进行插入操作的一端称为队尾出队......