背景知识

腐蚀与膨胀基本原理:就是用一个特定的结构元素来与待处理图像按像素做逻辑操作;可以理解成拿一个带孔的网格板(结构元素矩阵中元素为1的为孔)盖住图像的某一部分,然后按照各种不同的观察方式来确定操作类型。

比如:腐蚀操作就是拿这个结构元素的中心位置(假设参与逻辑计算的元素对应与二维矩阵中元素为1的点,即网格板上的孔),在图像上移动时,如果透过所有的孔都能看到底下的图像,那么这个中心点处的图像就保留,否则去除。

腐蚀

图像腐蚀运算定义

二值图像腐蚀运算的数学表达式为

g(x,y)=erode[f(x, y ), B]=AND[Bf(x,y)]

其中,g(x,y)为腐蚀后的二值图像,f(x,y)为原二值图像,B为结构元素。B(x,y)定义为:

Bf(x,y)={f(x-bx, y-by) ,(bx, by)∈B}

算子AND(x1,…,xn)定义为:当且仅当x1=··=xn=1时,AND(x1,…,xn)等于1;否则为0。

把结构元素B平移a后得到Ba,若Ba包含于X,我们记下这个a点,所有满足上述条件的a点组成的集合称做X被B腐蚀(Erosion)的结果。用公式表示为:E(X)={a| Ba∈X}=XB。原理图如下:

实际使用时示意图:

说明:左边是被处理的图象X(二值图象,我们针对的是黑点),中间是结构元素B,那个标有origin的点是中心点,即当前处理元素的位置,我们在介绍模板操作时也有过类似的概念。腐蚀的方法是,拿B的中心点和X上的点一个一个地对比,如果B上的所有点都在X的范围内,则该点保留,否则将该点去掉;右边是腐蚀后的结果。可以看出,它仍在原来X的范围内,且比X包含的点要少,就象X被腐蚀掉了一层。

        private void erode_Click(object sender, EventArgs e)
{
if (curBitmap != null)
{
struction struForm = new struction();
struForm.Text = "腐蚀运算结构元素";
if (struForm.ShowDialog() == DialogResult.OK)
{
Rectangle rect = new Rectangle(, , curBitmap.Width, curBitmap.Height);
BitmapData bmpData = curBitmap.LockBits(rect, ImageLockMode.ReadWrite, curBitmap.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = curBitmap.Width * curBitmap.Height;
byte[] grayValues = new byte[bytes];
Marshal.Copy(ptr, grayValues, , bytes); //得到结构元素
byte flagStru = struForm.GetStruction; byte[] tempArray = new byte[bytes];
for (int i = ; i < bytes; i++)
{
tempArray[i] = ;
}
switch (flagStru)
{
case 0x11:
//3位水平方向结构元素
for (int i = ; i < curBitmap.Height; i++)
{
for (int j = ; j < curBitmap.Width - ; j ++)
{
if (grayValues[i * curBitmap.Width + j] == &&
grayValues[i * curBitmap.Width + j + ] == &&
grayValues[i * curBitmap.Width + j - ] == )
{
tempArray[i * curBitmap.Width + j] = ;
}
}
}
break;
case 0x21:
//5位水平方向结构元素
for (int i = ; i < curBitmap.Height; i++)
{
for (int j = ; j < curBitmap.Width - ; j++)
{
if (grayValues[i * curBitmap.Width + j] == &&
grayValues[i * curBitmap.Width + j + ] == &&
grayValues[i * curBitmap.Width + j - ] == &&
grayValues[i * curBitmap.Width + j + ] == &&
grayValues[i * curBitmap.Width + j - ] == )
{
tempArray[i * curBitmap.Width + j] = ;
}
}
}
break;
case 0x12:
//3位垂直方向结构元素
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width; j++)
{
if (grayValues[i * curBitmap.Width + j] == &&
grayValues[(i - ) * curBitmap.Width + j] == &&
grayValues[(i + ) * curBitmap.Width + j] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x22:
//5位垂直方向结构元素
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width; j++)
{
if (grayValues[i * curBitmap.Width + j] == &&
grayValues[(i - ) * curBitmap.Width + j] == &&
grayValues[(i + ) * curBitmap.Width + j] == &&
grayValues[(i - ) * curBitmap.Width + j] == &&
grayValues[(i + ) * curBitmap.Width + j] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x14:
//3位十字形状结构元素
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width - ; j++)
{
if (grayValues[i * curBitmap.Width + j] == &&
grayValues[(i - ) * curBitmap.Width + j] == &&
grayValues[(i + ) * curBitmap.Width + j] == &&
grayValues[i * curBitmap.Width + j + ] == &&
grayValues[i * curBitmap.Width + j - ] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x24:
//5位十字形状结构元素
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width - ; j++)
{
if (grayValues[i * curBitmap.Width + j] == &&
grayValues[(i - ) * curBitmap.Width + j] == &&
grayValues[(i + ) * curBitmap.Width + j] == &&
grayValues[(i - ) * curBitmap.Width + j] == &&
grayValues[(i + ) * curBitmap.Width + j] == &&
grayValues[i * curBitmap.Width + j + ] == &&
grayValues[i * curBitmap.Width + j - ] == &&
grayValues[i * curBitmap.Width + j + ] == &&
grayValues[i * curBitmap.Width + j - ] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x18:
//3位方形结构元素
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width - ; j++)
{
if (grayValues[i * curBitmap.Width + j] == &&
grayValues[(i - ) * curBitmap.Width + j] == &&
grayValues[(i + ) * curBitmap.Width + j] == &&
grayValues[i * curBitmap.Width + j + ] == &&
grayValues[i * curBitmap.Width + j - ] == &&
grayValues[(i - ) * curBitmap.Width + j - ] == &&
grayValues[(i + ) * curBitmap.Width + j - ] == &&
grayValues[(i - ) * curBitmap.Width + j + ] == &&
grayValues[(i + ) * curBitmap.Width + j + ] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x28:
//5位方形结构元素
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width - ; j++)
{
if (grayValues[(i - ) * curBitmap.Width + j - ] == &&
grayValues[(i - ) * curBitmap.Width + j - ] == &&
grayValues[(i - ) * curBitmap.Width + j] == &&
grayValues[(i - ) * curBitmap.Width + j + ] == &&
grayValues[(i - ) * curBitmap.Width + j + ] == &&
grayValues[(i - ) * curBitmap.Width + j - ] == &&
grayValues[(i - ) * curBitmap.Width + j - ] == &&
grayValues[(i - ) * curBitmap.Width + j] == &&
grayValues[(i - ) * curBitmap.Width + j + ] == &&
grayValues[(i - ) * curBitmap.Width + j + ] == &&
grayValues[i * curBitmap.Width + j - ] == &&
grayValues[i * curBitmap.Width + j - ] == &&
grayValues[i * curBitmap.Width + j] == &&
grayValues[i * curBitmap.Width + j + ] == &&
grayValues[i * curBitmap.Width + j + ] == &&
grayValues[(i + ) * curBitmap.Width + j - ] == &&
grayValues[(i + ) * curBitmap.Width + j - ] == &&
grayValues[(i + ) * curBitmap.Width + j] == &&
grayValues[(i + ) * curBitmap.Width + j + ] == &&
grayValues[(i + ) * curBitmap.Width + j + ] == &&
grayValues[(i + ) * curBitmap.Width + j - ] == &&
grayValues[(i + ) * curBitmap.Width + j - ] == &&
grayValues[(i + ) * curBitmap.Width + j] == &&
grayValues[(i + ) * curBitmap.Width + j + ] == &&
grayValues[(i + ) * curBitmap.Width + j + ] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
default:
MessageBox.Show("错误的结构元素!");
break;
} grayValues = (byte[])tempArray.Clone();
Marshal.Copy(grayValues, , ptr, bytes);
curBitmap.UnlockBits(bmpData);
} Invalidate();
}
}
 #region 关于图像尺寸的说明

        //本代码只能处理8位深度的512*512图像。可自行修改,如修改3位水平方向结构元素代码:

        //01修改成如下代码即可处理任意尺寸的8位深度的图像
//int bytes = bmpData.Stride * curBitmap.Height;
//for (int i = 0; i < curBitmap.Height; i++)
//{
// for (int j = 1; j < curBitmap.Width - 1; j++)
// {
// if (grayValues[i * bmpData.Stride + j] == 0 &&
// grayValues[i * bmpData.Stride + j + 3] == 0 &&
// grayValues[i * bmpData.Stride + j - 1] == 0)
// {
// tempArray[i * bmpData.Stride + j] = 0;
// tempArray[i * bmpData.Stride + j + 1] = 0;
// tempArray[i * bmpData.Stride + j + 2] = 0;
// }
// }
//} //02修改成如下代码即可处理任意尺寸的24位深度的图像
//int bytes = bmpData.Stride * curBitmap.Height;
//for (int i = 0; i < curBitmap.Height; i++)
//{
// for (int j = 4; j < curBitmap.Width * 3 - 3; j += 3)
// {
// if (grayValues[i * bmpData.Stride + j] == 0 &&
// grayValues[i * bmpData.Stride + j + 3] == 0 &&
// grayValues[i * bmpData.Stride + j - 1] == 0)
// {
// tempArray[i * bmpData.Stride + j] = 0;
// tempArray[i * bmpData.Stride + j + 1] = 0;
// tempArray[i * bmpData.Stride + j + 2] = 0;
// }
// }
//}
#endregion

膨胀

图像膨胀运算定义

二值图像膨胀运算的数学表达式为:

g(x, y)=dilate[f(x, y), B]=OR[Bf(x,y)]

其中,g(x,y)为膨胀后的二值图像,f(x,y)为原二值图像,B为结构元素。

B(x,y)定义为:

Bf(x,y)={f(x-bx, y-by) ,(bx, by)∈B}

算子OR(x1…xn)定义为:当且仅当x1=…=xn=0时,OR(x1,…xn)等于0;否则为1

膨胀(dilation)可以看做是腐蚀的对偶运算,其定义是:把结构元素B平移a后得到Ba,若Ba击中X,我们记下这个a点。所有满足上述条件的a点组成的集合称做X被B膨胀的结果。用公式表示为:D(X)={a | Ba↑X}=X  B,如图6.13所示。图6.13中X是被处理的对象,B是结构元素,不难知道,对于任意一个在阴影部分的点a,Ba击中X,所以X被B膨胀的结果就是那个阴影部分。阴影部分包括X的所有范围,就象X膨胀了一圈似的,这就是为什么叫膨胀的原因。原理图如下:

实际使用时示意图:

说明:左边是被处理的图象X(二值图象,我们针对的是黑点),中间是结构元素B。膨胀的方法是,拿B的中心点和X上的点及X周围的点一个一个地对,如果B上有一个点落在X的范围内,则该点就为黑;右边是膨胀后的结果。可以看出,它包括X的所有范围,就象X膨胀了一圈似的。

private void dilate_Click(object sender, EventArgs e)
{
if (curBitmap != null)
{
struction struForm = new struction();
struForm.Text = "膨胀运算结构元素";
if (struForm.ShowDialog() == DialogResult.OK)
{
Rectangle rect = new Rectangle(, , curBitmap.Width, curBitmap.Height);
BitmapData bmpData = curBitmap.LockBits(rect, ImageLockMode.ReadWrite, curBitmap.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = curBitmap.Width * curBitmap.Height;
byte[] grayValues = new byte[bytes];
Marshal.Copy(ptr, grayValues, , bytes); byte flagStru = struForm.GetStruction; byte[] tempArray = new byte[bytes];
for (int i = ; i < bytes; i++)
{
tempArray[i] = ;
} switch (flagStru)
{
case 0x11:
for (int i = ; i < curBitmap.Height; i++)
{
for (int j = ; j < curBitmap.Width - ; j++)
{
if (grayValues[i * curBitmap.Width + j] == ||
grayValues[i * curBitmap.Width + j + ] == ||
grayValues[i * curBitmap.Width + j - ] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x21:
for (int i = ; i < curBitmap.Height; i++)
{
for (int j = ; j < curBitmap.Width - ; j++)
{
if (grayValues[i * curBitmap.Width + j] == ||
grayValues[i * curBitmap.Width + j + ] == ||
grayValues[i * curBitmap.Width + j - ] == ||
grayValues[i * curBitmap.Width + j + ] == ||
grayValues[i * curBitmap.Width + j - ] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x12:
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width; j++)
{
if (grayValues[i * curBitmap.Width + j] == ||
grayValues[(i - ) * curBitmap.Width + j] == ||
grayValues[(i + ) * curBitmap.Width + j] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x22:
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width; j++)
{
if (grayValues[i * curBitmap.Width + j] == ||
grayValues[(i - ) * curBitmap.Width + j] == ||
grayValues[(i + ) * curBitmap.Width + j] == ||
grayValues[(i - ) * curBitmap.Width + j] == ||
grayValues[(i + ) * curBitmap.Width + j] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x14:
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width - ; j++)
{
if (grayValues[i * curBitmap.Width + j] == ||
grayValues[(i - ) * curBitmap.Width + j] == ||
grayValues[(i + ) * curBitmap.Width + j] == ||
grayValues[i * curBitmap.Width + j + ] == ||
grayValues[i * curBitmap.Width + j - ] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x24:
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width - ; j++)
{
if (grayValues[i * curBitmap.Width + j] == ||
grayValues[(i - ) * curBitmap.Width + j] == ||
grayValues[(i + ) * curBitmap.Width + j] == ||
grayValues[(i - ) * curBitmap.Width + j] == ||
grayValues[(i + ) * curBitmap.Width + j] == ||
grayValues[i * curBitmap.Width + j + ] == ||
grayValues[i * curBitmap.Width + j - ] == ||
grayValues[i * curBitmap.Width + j + ] == ||
grayValues[i * curBitmap.Width + j - ] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x18:
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width - ; j++)
{
if (grayValues[i * curBitmap.Width + j] == ||
grayValues[(i - ) * curBitmap.Width + j] == ||
grayValues[(i + ) * curBitmap.Width + j] == ||
grayValues[i * curBitmap.Width + j + ] == ||
grayValues[i * curBitmap.Width + j - ] == ||
grayValues[(i - ) * curBitmap.Width + j - ] == ||
grayValues[(i + ) * curBitmap.Width + j - ] == ||
grayValues[(i - ) * curBitmap.Width + j + ] == ||
grayValues[(i + ) * curBitmap.Width + j + ] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
case 0x28:
for (int i = ; i < curBitmap.Height - ; i++)
{
for (int j = ; j < curBitmap.Width - ; j++)
{
if (grayValues[(i - ) * curBitmap.Width + j - ] == ||
grayValues[(i - ) * curBitmap.Width + j - ] == ||
grayValues[(i - ) * curBitmap.Width + j] == ||
grayValues[(i - ) * curBitmap.Width + j + ] == ||
grayValues[(i - ) * curBitmap.Width + j + ] == ||
grayValues[(i - ) * curBitmap.Width + j - ] == ||
grayValues[(i - ) * curBitmap.Width + j - ] == ||
grayValues[(i - ) * curBitmap.Width + j] == ||
grayValues[(i - ) * curBitmap.Width + j + ] == ||
grayValues[(i - ) * curBitmap.Width + j + ] == ||
grayValues[i * curBitmap.Width + j - ] == ||
grayValues[i * curBitmap.Width + j - ] == ||
grayValues[i * curBitmap.Width + j] == ||
grayValues[i * curBitmap.Width + j + ] == ||
grayValues[i * curBitmap.Width + j + ] == ||
grayValues[(i + ) * curBitmap.Width + j - ] == ||
grayValues[(i + ) * curBitmap.Width + j - ] == ||
grayValues[(i + ) * curBitmap.Width + j] == ||
grayValues[(i + ) * curBitmap.Width + j + ] == ||
grayValues[(i + ) * curBitmap.Width + j + ] == ||
grayValues[(i + ) * curBitmap.Width + j - ] == ||
grayValues[(i + ) * curBitmap.Width + j - ] == ||
grayValues[(i + ) * curBitmap.Width + j] == ||
grayValues[(i + ) * curBitmap.Width + j + ] == ||
grayValues[(i + ) * curBitmap.Width + j + ] == )
{
tempArray[i * curBitmap.Width + j] = ;
} }
}
break;
default:
MessageBox.Show("错误的结构元素!");
break;
} grayValues = (byte[])tempArray.Clone(); System.Runtime.InteropServices.Marshal.Copy(grayValues, , ptr, bytes);
curBitmap.UnlockBits(bmpData);
} Invalidate();
}
}
#region 关于图像尺寸的说明

        //本代码只能处理8位深度的512*512图像。可自行修改,例如修改3位水平方向结构元素代码:

        //01修改成如下代码即可处理任意尺寸的8位深度的图像
//int bytes = bmpData.Stride * curBitmap.Height;
//for (int i = 0; i < curBitmap.Height; i++)
//{
// for (int j = 1; j < curBitmap.Width - 1; j++)
// {
// if (grayValues[i * bmpData.Stride + j] == 0 ||
// grayValues[i * bmpData.Stride + j + 3] == 0 ||
// grayValues[i * bmpData.Stride + j - 1] == 0)
// {
// tempArray[i * bmpData.Stride + j] = 0;
// tempArray[i * bmpData.Stride + j + 1] = 0;
// tempArray[i * bmpData.Stride + j + 2] = 0;
// }
// }
//} //02修改成如下代码即可处理任意尺寸的24位深度的图像
//int bytes = bmpData.Stride * curBitmap.Height;
//for (int i = 0; i < curBitmap.Height; i++)
//{
// for (int j = 4; j < curBitmap.Width * 3 - 3; j += 3)
// {
// if (grayValues[i * bmpData.Stride + j] == 0 ||
// grayValues[i * bmpData.Stride + j + 3] == 0 ||
// grayValues[i * bmpData.Stride + j - 1] == 0)
// {
// tempArray[i * bmpData.Stride + j] = 0;
// tempArray[i * bmpData.Stride + j + 1] = 0;
// tempArray[i * bmpData.Stride + j + 2] = 0;
// }
// }
//}
#endregion

c#数字图像处理(十二)图像的腐蚀与膨胀的更多相关文章

  1. Win8MetroC#数字图像处理--2.2图像二值化函数

    原文:Win8MetroC#数字图像处理--2.2图像二值化函数 [函数代码] /// <summary> /// Binary process. /// </summary> ...

  2. Win8 Metro(C#)数字图像处理--3.2图像方差计算

    原文:Win8 Metro(C#)数字图像处理--3.2图像方差计算 /// <summary> /// /// </summary>Variance computing. / ...

  3. Win8 Metro(C#)数字图像处理--3.3图像直方图计算

    原文:Win8 Metro(C#)数字图像处理--3.3图像直方图计算 /// <summary> /// Get the array of histrgram. /// </sum ...

  4. Win8 Metro(C#)数字图像处理--3.4图像信息熵计算

    原文:Win8 Metro(C#)数字图像处理--3.4图像信息熵计算 [函数代码] /// <summary> /// Entropy of one image. /// </su ...

  5. Win8 Metro(C#)数字图像处理--3.5图像形心计算

    原文:Win8 Metro(C#)数字图像处理--3.5图像形心计算 /// <summary> /// Get the center of the object in an image. ...

  6. Win8 Metro(C#)数字图像处理--3.1图像均值计算

    原文:Win8 Metro(C#)数字图像处理--3.1图像均值计算 /// <summary> /// Mean value computing. /// </summary> ...

  7. Win8 Metro(C#)数字图像处理--2.74图像凸包计算

    原文:Win8 Metro(C#)数字图像处理--2.74图像凸包计算 /// <summary> /// Convex Hull compute. /// </summary> ...

  8. Win8 Metro(C#)数字图像处理--2.68图像最小值滤波器

    原文:Win8 Metro(C#)数字图像处理--2.68图像最小值滤波器 /// <summary> /// Min value filter. /// </summary> ...

  9. Win8 Metro(C#)数字图像处理--2.52图像K均值聚类

    原文:Win8 Metro(C#)数字图像处理--2.52图像K均值聚类  [函数名称]   图像KMeans聚类      KMeansCluster(WriteableBitmap src,i ...

  10. Win8 Metro(C#)数字图像处理--2.45图像雾化效果算法

    原文:Win8 Metro(C#)数字图像处理--2.45图像雾化效果算法 [函数名称]   图像雾化         AtomizationProcess(WriteableBitmap src,i ...

随机推荐

  1. 查漏补缺:2020年搞定SpringCloud面试(含答案和思维导图)

    前言 Spring Cloud是一系列框架的有序集合.它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册.配置中心.消息总线.负载均衡.断路器.数据监控等,都 ...

  2. 【Composer】PHP开发者必须了解!

    Composer是一个非常流行的PHP包依赖管理工具,已经取代PEAR包管理器,对于PHP开发者来说掌握Composer是必须的. 对于使用者来说Composer非常的简单,通过简单的一条命令将需要的 ...

  3. Java8 LocalDate计算两个日期的间隔天数

    Java8新增了java.time包,提供了很多新封装好的类,使我们可以摆脱原先使用java.util.Time以及java.util.Calendar带来的复杂. 其中LocalDate正是本文中使 ...

  4. 【题解】P1712 [NOI2016]区间(贪心+线段树)

    [题解]P1712 [NOI2016]区间(贪心+线段树) 一个observe是,对于一个合法的方案,将其线段长度按照从大到小排序后,他极差的来源是第一个和最后一个.或者说,读入的线段按照长度分类后, ...

  5. Redo与Undo的理解

    本文概要本文的原意是一篇个人学习笔记,为了避免成为草草记录一下的流水账,尝试从给人介绍的角度开写.但在整理的过程中,越来越感觉力不从心,一是细节太多了,原以为足够了解的一个小知识点下可能隐藏了很多细节 ...

  6. JVM探秘:垃圾收集算法

    本系列笔记主要基于<深入理解Java虚拟机:JVM高级特性与最佳实践 第2版>,是这本书的读书笔记. 垃圾收集算法 垃圾收集算法主要有标记-清除算法.复制算法.标记-整理算法.分代收集算法 ...

  7. 并发编程的基石——CAS机制

    本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. 并发编程系列博客传送门 Java中提供了很多原子操作类来保证共享变量操作的原子性 ...

  8. 极光推送SDK通过泰尔终端实验室检测,符合统一推送接口标准

    1月7日,中国深圳--国内领先的开发者服务提供商极光(Aurora Mobile, NASDAQ:JG)宣布其旗下产品极光推送SDK通过中国信息通信研究院泰尔终端实验室的检测,其性能和接口标准符合统一 ...

  9. docker-网桥

    使用桥接网络 在网络方面,桥接网络是链路层设备,它在网络段之间转发流量. 网桥可以是硬件设备或在主机内核中运行的软件设备. Docker而言,桥接网络使用软件桥接器,该软件桥接器允许连接到同一桥接网络 ...

  10. Java小项目之:五子棋,你下棋下得过电脑吗?

    Java小项目之:五子棋,你下棋下得过电脑吗? Java五子棋功能要求: 1.创建窗口和设计一个棋盘界面 2.实现鼠标点击,棋子出现,黑白棋轮流下 3.能够判断五子相连输赢 4.添加重新开始,悔棋,退 ...