下面blabla一段废话心急的同志们可以跳过。
周所周知,实际获得的图像再形成、传输、接收和处理的过程中,不可避免地存在着外部干扰和内部干扰,如光电转换过程中敏感原件灵敏度的不均匀性、数字化过程的量化噪声、传输过程中的误差以及人为因素等,均会存在一定程度的噪声干扰。噪声恶化了图像质量,使图像模糊,特征淹没,给分析带来困难。
噪声干扰一般是随机产生的,分布不规则,大小也不规则。噪声像素的灰度是空间不相关,与临近的像素显著不同。因此,去除噪声、恢复原始图像是图像处理中的一个重要内容。去除图像噪声的工作称为图像平滑或滤波。图像去噪是一种信号滤波的方法,目的是消除噪声,保留游泳信号,降低干扰,改善图像质量。同时,在提取较大目标前,去除太小的细节,或将目标内的小间断连接起来,平滑也起到模糊作用。平滑滤波对图像的低频分量增强,同时削弱高频分量,用于消除图像中的随机噪声,起到平滑作用。
由于噪声源众多(如光栅扫描、底片颗粒、机械原件、信道传输等),噪声种类复杂(如加强噪声、乘性噪声、量化噪声等),所以平滑方法也多种多样。平滑可以在空间域进行,也可以在频率域进行。空间域法采用在原图像上直接对像素的灰度值进行处理,分为两类:点运算和局部运算。
下面介绍记中图像平滑的常用方法。
tips:几乎下面的所有处理函数均会用到此方法,为方便广大读者动手实践,特记录在此
其作用是将传入的图像以100为阈值进行二值化,并返回二值化之后的图像。
private Bitmap twoGray(Bitmap bm) {
int width = bm.getWidth();
int height = bm.getHeight();
int color;
int r, g, b, a;
Bitmap bmp = Bitmap.createBitmap(width, height
, Bitmap.Config.ARGB_8888);
int[] oldPx = new int[width * height];
int[] newPx = new int[width * height];
bm.getPixels(oldPx, 0, width, 0, 0, width, height);
for (int i = 0; i < width * height; i++) {
color = oldPx[i];
r = Color.red(color);
g = Color.green(color);
b = Color.blue(color);
a = Color.alpha(color);
int gray = (int)((float)r*0.3+(float)g*0.59+(float)b*0.11);
if(gray > 100) {
gray = 255;
} else {
gray = 0;
}
newPx[i] = Color.argb(a,gray,gray,gray);
}
bmp.setPixels(newPx, 0, width, 0, 0, width, height);
return bmp;
}
一、二值图像的黑白点噪声滤波
本函数消除二值图像f(i,j)上的黑白点噪声,当f(i,j)周围的8个像素的平均值为a时,若|f(i,j)-a|的值大于127.5,则对f(i,j)的黑白进行反转;若小于127.5,则f(i,j)不变。
思路:
- 传入需要处理的图像
- 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
- 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
- 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
- 计算所需处理点周围8个点灰度的平均值average
- 所需处理点的灰度值为gray,如果|gray-average|>127.5,则令gray=average
- 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
- 创建一个高度、宽度和原图完全一样的新图
- 将存新数组中的颜色值赋给新图
- 将新图像返回
private void twoGrayWhiteAndBlack(Bitmap bm) {
Bitmap grayBitmap = twoGray(bm);
int width = bm.getWidth();
int height = bm.getHeight();
int color,gray,average;
int r, g, b, a = 0;
Bitmap bmp = Bitmap.createBitmap(width, height
, Bitmap.Config.ARGB_8888);
int[] oldPx = new int[width * height];
int[] newPx = new int[width * height];
grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);
for (int i = 1; i < height - 1; i++) {
for(int j = 1;j < width - 1;j++) {
color = oldPx[i*width+j];
r = Color.red(color);//已经进行了二值化,因此r,g,b分量都等于灰度值
a = Color.alpha(color);
average = 0;
average = (int)((Color.red(oldPx[(i-1)*width+j-1]) + Color.red(oldPx[(i-1)*width+j]) + Color.red(oldPx[(i-1)*width+j+1])
+ Color.red(oldPx[(i)*width+j-1]) + Color.red(oldPx[(i)*width+j+1]) + Color.red(oldPx[(i+1)*width+j-1])
+ Color.red(oldPx[(i+1)*width+j]) + Color.red(oldPx[(i+1)*width+j+1])) / 8);
//Log.d("xyz","average = " + average + " i = " + i*width+j);
if(Math.abs(r - average) > 127.5) {
r = average;
}
newPx[i*width+j] = Color.argb(a,r,r,r);
}
}
bmp.setPixels(newPx, 0, width, 0, 0, width, height);
mImageView.setImageBitmap(bmp);
}
效果(为防止大家看不清楚,先来两张细节图)
二、消除孤立黑像素点
一幅图像往往可能受到各种噪声源的干扰,这些噪声使图像表现为一些孤立像素点,它们像雪花落在画面上一样。
本函数在二值图像f中消除孤立于周围的黑像素点(变成白的)。像素的四邻域和八邻域关系如下所示
在四邻域的情况下,若黑像素f(i,j)的上下左右4个像素全为白(255),则f(i,j)也取255。在八邻域的情况下,若黑像素f(i,j)的周围8个像素全为白(255),则f(i,j)也取255。
思路:
- 传入需要处理的图像
- 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
- 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
- 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
- 计算所需处理点周围8(或4)个点灰度的和sum
- 所需处理点灰度值为gray,如果sum>255*8(或sum>255*4),则令gray = 255
- 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
- 创建一个高度、宽度和原图完全一样的新图
- 将存新数组中的颜色值赋给新图
- 将新图像返回
private void isolatedBlack(Bitmap bm, int count) {
Bitmap grayBitmap = twoGray(bm);
int width = bm.getWidth();
int height = bm.getHeight();
int color,gray,average;
int r, g, b, a = 0;
Bitmap bmp = Bitmap.createBitmap(width, height
, Bitmap.Config.ARGB_8888);
int[] oldPx = new int[width * height];
int[] newPx = new int[width * height];
grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);
if(count == 4) {
for(int i = 1;i < height - 1;i++) {
for (int j = 1;j < width;j++) {
color = oldPx[i*width+j];
r = Color.red(color);//已经进行了二值化,因此r,g,b分量都等于灰度值
a = Color.alpha(color);
if(r == 255)
continue;
if(Color.red(oldPx[(i-1)*width+j]) + Color.red(oldPx[(i)*width+j-1]) + Color.red(oldPx[(i)*width+j+1])
+ Color.red(oldPx[(i+1)*width+j]) == 255 * 4) {
r = 255;
}
newPx[i*width+j] = Color.argb(a,r,r,r);
}
}
}
if(count == 8) {
for(int i = 1;i < height - 1;i++) {
for (int j = 1;j < width;j++) {
color = oldPx[i*width+j];
r = Color.red(color);//已经进行了二值化,因此r,g,b分量都等于灰度值
a = Color.alpha(color);
if(r == 255)
continue;
if((Color.red(oldPx[(i-1)*width+j-1]) + Color.red(oldPx[(i-1)*width+j]) + Color.red(oldPx[(i-1)*width+j+1])
+ Color.red(oldPx[(i)*width+j-1]) + Color.red(oldPx[(i)*width+j+1]) + Color.red(oldPx[(i+1)*width+j-1])
+ Color.red(oldPx[(i+1)*width+j]) + Color.red(oldPx[(i+1)*width+j+1])) == 255 * 8) {
r = 255;
}
newPx[i*width+j] = Color.argb(a,r,r,r);
}
}
}
bmp.setPixels(newPx, 0, width, 0, 0, width, height);
mImageView.setImageBitmap(bmp);
}
第二行用到的twoGray()函数记录在本文上面。
效果(四邻域)
三、邻域平均法
一幅图像往往受到各种噪声的干扰,噪声常为一些孤立的像素点,往往是叠加在图像上的随机噪声,像雪花一样使图像被污染,而图像灰度应该是相对连续变化的,一般不会突然变大或变小,这种噪声可以用邻域平均法是它得到抑制。噪声点像素的灰度与它们临近像素有显著不同,根据噪声点的这一空间特性,我们用邻域平均法和阈值平均法进行处理。
(一)3*3均值滤波器
*
*
*
*
模板一 模板二 模板三 模板四
将所需处理点及其周围8个点按照模板设置的权值求加权平均值,在除以总权值,即为处理后该点的灰度值,我们选择最简单的模板三。
private void average33(Bitmap bm) {
Bitmap grayBitmap = twoGray(bm);
int width = bm.getWidth();
int height = bm.getHeight();
int color,gray,average;
int r, g, b, a = 0;
Bitmap bmp = Bitmap.createBitmap(width, height
, Bitmap.Config.ARGB_8888);
int[] oldPx = new int[width * height];
int[] newPx = new int[width * height];
grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);
for (int i = 1; i < height - 1; i++) {
for(int j = 1;j < width - 1;j++) {
color = oldPx[i*width+j];
a = Color.alpha(color);
average = 0;
average = (int)((Color.red(oldPx[(i-1)*width+j-1]) + Color.red(oldPx[(i-1)*width+j]) + Color.red(oldPx[(i-1)*width+j+1])
+ Color.red(oldPx[(i)*width+j-1]) + Color.red(oldPx[(i)*width+j+1]) + Color.red(oldPx[(i+1)*width+j-1])
+ Color.red(oldPx[(i+1)*width+j]) + Color.red(oldPx[(i+1)*width+j+1])) / 8);
newPx[i*width+j] = Color.argb(a,average,average,average);
}
}
bmp.setPixels(newPx, 0, width, 0, 0, width, height);
mImageView.setImageBitmap(bmp);
}
第二行用到的twoGray()函数记录在本文上面。
效果
(二)N×N均值滤波
当灰度图像f中以像素f(i,j)为中心的N×N屏蔽窗口(N=3,5,7,……)内平均灰度为average时,无条件作f(i,j)=average处理,N由用户给定,且取N值越大,噪声被减少越明显。但此方法是以图像的模糊为代价的。
思路
- 传入需要处理的图像
- 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
- 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
- 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
- 取得N值
- 算出以该点为中心的N×N屏蔽窗口内平均值。
- 把该点灰度值置为平均值
- 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
- 创建一个高度、宽度和原图完全一样的新图
- 将存新数组中的颜色值赋给新图
- 将新图像返回
private void averagenn(final Bitmap bm) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("n*n均值滤波法");
builder.setMessage("请输入n(奇数)");
View view1 = LayoutInflater.from(getContext()).inflate(R.layout.threshold,null);
final EditText mSingleThresholdEt = view1.findViewById(R.id.digit_dialog);
builder.setView(view1);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Bitmap grayBitmap = twoGray(bm);
int width = bm.getWidth();
int height = bm.getHeight();
int color,gray,average;
int r, g, b, a = 0;
int xx,yy,n2 = 0,sum;
Bitmap bmp = Bitmap.createBitmap(width, height
, Bitmap.Config.ARGB_8888);
int[] oldPx = new int[width * height];
int[] newPx = new int[width * height];
grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);
String str = mSingleThresholdEt.getText().toString();
if("".equals(str)) {
n = 0;
} else {
n = Integer.valueOf(str);
}
if(n < 3 || n % 2 != 1) {
return;
}
if(n >= 3 && n % 2 == 1) {
n2 = (n - 1) / 2;
}
for (int j = n2;j < height - n2;j++) {
for(int k = n2;k < width - n2;k++) {
color = oldPx[j*width+k];
a = Color.alpha(color);
sum = 0;
for(yy = j - n2;yy <= j + n2;yy++) {
for(xx = k - n2;xx <= k + n2;xx++) {
sum += Color.red(oldPx[yy * width + xx]);
}
}
r = (int)((float)sum/(n*n) + 0.5f);
//Log.d("xyz","sum = " + sum + " i = " + j*width+k);
newPx[j*width+k] = Color.argb(a,r,r,r);
}
}
bmp.setPixels(newPx, 0, width, 0, 0, width, height);
mImageView.setImageBitmap(bmp);
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
下面是我们在接收用户输入时使用的对话框所引入的自定义布局threshold.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="阈值:"/>
<EditText
android:id="@+id/digit_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
效果
(三)超限邻域平均法
邻域平均法虽然简单,但它存在着边缘模糊效应,本来不是噪声的边缘处,应该保留原有的灰度差,而邻域平均法使边缘处的灰度趋向均匀,造成了边缘模糊。为了减少模糊效应,需寻求改进的途径,力求找到解决清除噪声和边缘模糊这对矛盾的最佳方法。
阈值邻域平均法以某个灰度值T作为阈值,如果某个像素的灰度大于其临近像素的平均值,并超过阈值,才使用平均灰度值置换这个像素灰度,它的数学表达式为
此式表明,若某点值与其邻域平均值相差超过T,则用平均值代替,进行平均处理,可去除噪声;否则还保留原值,不进行平均处理,从而减少模糊。这种算法对抑制椒盐噪声比较有效,同时也能较好地保留仅有微小灰度差的图像细节。
思路
- 传入需要处理的图像
- 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
- 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
- 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
- 取得T值
- 算出以该点为中心的N×N屏蔽窗口内平均值average。
- 该点灰度值为gray,如果|gray-average|>T,则将该点灰度值置换为average
- 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
- 创建一个高度、宽度和原图完全一样的新图
- 将存新数组中的颜色值赋给新图
- 将新图像返回
private void overLimitAverage(final Bitmap bm) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("超限邻域平均法");
builder.setMessage("请输入阈值");
View view1 = LayoutInflater.from(getContext()).inflate(R.layout.threshold,null);
final EditText mSingleThresholdEt = view1.findViewById(R.id.digit_dialog);
builder.setView(view1);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int k) {
Bitmap grayBitmap = twoGray(bm);
int width = bm.getWidth();
int height = bm.getHeight();
int color,gray,average;
int r, g, b, a = 0;
Bitmap bmp = Bitmap.createBitmap(width, height
, Bitmap.Config.ARGB_8888);
int[] oldPx = new int[width * height];
int[] newPx = new int[width * height];
grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);
String str = mSingleThresholdEt.getText().toString();
if("".equals(str)) {
T = 0;
} else {
T = Integer.valueOf(str);
}
for (int i = 1; i < height - 1; i++) {
for(int j = 1;j < width - 1;j++) {
color = oldPx[i*width+j];
r = Color.red(color);
a = Color.alpha(color);
average = 0;
average = (int)((Color.red(oldPx[(i-1)*width+j-1]) + Color.red(oldPx[(i-1)*width+j]) + Color.red(oldPx[(i-1)*width+j+1])
+ Color.red(oldPx[(i)*width+j-1]) + Color.red(oldPx[(i)*width+j+1]) + Color.red(oldPx[(i+1)*width+j-1])
+ Color.red(oldPx[(i+1)*width+j]) + Color.red(oldPx[(i+1)*width+j+1])) / 8);
if(Math.abs(r - average) > T) {
r = average;
}
newPx[i*width+j] = Color.argb(a,r,r,r);
}
}
bmp.setPixels(newPx, 0, width, 0, 0, width, height);
mImageView.setImageBitmap(bmp);
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
下面是我们在接收用户输入时使用的对话框所引入的自定义布局threshold.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="阈值:"/>
<EditText
android:id="@+id/digit_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
效果
四、中值滤波法
中值滤波能够抑制噪声又保持细节,是比较好的选择。
对一些细节特别多,尤其是点、线、尖顶细节过的图像,不宜采用中值滤波方法。
如下图所示,取3×3窗口,从小到大排列:
33 200 201 202 205 206 207 208 210
取中间值205代替原来的数值202。
二维中值滤波的窗口形状和尺寸设计对滤波的效果影响较大,对于不同的图像内容和不同的应用要求,往往采用不同的形状和尺寸。
常用的二维中值滤波窗口有线状、方形、圆形、十字型及圆环形等。
(一)N×N中值滤波
思路
- 传入需要处理的图像
- 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
- 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
- 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
- 取得N值
- 循环,对以某点为中心的N×N屏蔽窗口包括各点灰度值进行排序,得到中间值
- 把该点灰度值置为中间值
- 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
- 创建一个高度、宽度和原图完全一样的新图
- 将存新数组中的颜色值赋给新图
- 将新图像返回
private void middlenn(final Bitmap bm) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("n*n中值滤波");
builder.setMessage("请输入n值(大于3的奇数)");
View view1 = LayoutInflater.from(getContext()).inflate(R.layout.threshold,null);
final EditText mSingleThresholdEt = view1.findViewById(R.id.digit_dialog);
builder.setView(view1);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int k) {
Bitmap grayBitmap = twoGray(bm);
int width = bm.getWidth();
int height = bm.getHeight();
int color,gray,average;
int r, g, b, a = 0;
int yy,xx,n2 = 0,nn,chuo,chg = 0,m,medi,madom;
int[] mado = new int[1000];
Bitmap bmp = Bitmap.createBitmap(width, height
, Bitmap.Config.ARGB_8888);
int[] oldPx = new int[width * height];
int[] newPx = new int[width * height];
grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);
String str = mSingleThresholdEt.getText().toString();
if("".equals(str)) {
na = 0;
} else {
na = Integer.valueOf(str);
}
if(na < 3 || na % 2 != 1) {
Toast.makeText(getActivity(),"请输入一个大于等于3的奇数",Toast.LENGTH_SHORT).show();
return;
}
if(na >= 3 && na % 2 == 1) {
n2 = (na - 1) / 2;
}
nn = na * na;
chuo = (nn - 1) / 2;
for(int j = n2;j < height - n2;j++) {
for(int i = n2;i < width - n2;i++) {
m = 0;
color = oldPx[j*width+i];
a = Color.alpha(color);
for(yy = j - n2;yy <= j + n2;yy++) {
for (xx = i - n2; xx <= i + n2; xx++) {
mado[m] = Color.red(oldPx[yy * width + xx]);
m++;
}
}
//把mado[m]中的值按下降顺序用冒泡排序
do {
chg = 0;
for(m = 0;m < nn - 1;m++) {
if(mado[m] < mado[m+1]) {
madom = mado[m];
mado[m] = mado[m+1];
mado[m+1] = madom;
chg = 1;
}
}
}while(chg == 1);
//求中值medi
medi = mado[chuo];
//把中值代入显示图像中
//Log.d("xyz","medi + " + medi);
newPx[j*width+i] = Color.argb(a,medi,medi,medi);
}
}
bmp.setPixels(newPx, 0, width, 0, 0, width, height);
mImageView.setImageBitmap(bmp);
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
下面是我们在接收用户输入时使用的对话框所引入的自定义布局threshold.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="阈值:"/>
<EditText
android:id="@+id/digit_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
效果
(二)十字型中值滤波
思路
- 传入需要处理的图像
- 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
- 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
- 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
- 取得N值
- 循环,计算以某像素为中心的十字型屏蔽窗口(十字型的纵向和横向长度为N)内灰度中值
- 把该点灰度值置为中间值
- 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
- 创建一个高度、宽度和原图完全一样的新图
- 将存新数组中的颜色值赋给新图
- 将新图像返回
private void cross(final Bitmap bm) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("十字型中值滤波");
builder.setMessage("请输入n值(大于3的奇数)");
View view1 = LayoutInflater.from(getContext()).inflate(R.layout.threshold,null);
final EditText mSingleThresholdEt = view1.findViewById(R.id.digit_dialog);
builder.setView(view1);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int k) {
Bitmap grayBitmap = twoGray(bm);
int width = bm.getWidth();
int height = bm.getHeight();
int color,gray,average;
int r, g, b, a = 0;
int yy,xx,n2 = 0,nn,chuo,chg = 0,m,medi,madom;
int[] mado = new int[1000];
Bitmap bmp = Bitmap.createBitmap(width, height
, Bitmap.Config.ARGB_8888);
int[] oldPx = new int[width * height];
int[] newPx = new int[width * height];
grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);
String str = mSingleThresholdEt.getText().toString();
if("".equals(str)) {
nc = 0;
} else {
nc = Integer.valueOf(str);
}
if(nc < 3 || nc % 2 != 1) {
Toast.makeText(getActivity(),"请输入一个大于等于3的奇数",Toast.LENGTH_SHORT).show();
return;
}
if(nc >= 3 && nc % 2 == 1) {
n2 = (nc - 1) / 2;
}
nn = nc + nc + 1;
chuo = (nn - 1) / 2;
for(int j = n2;j < height - n2;j++) {
for(int i = n2;i < width - n2;i++) {
m = 0;
color = oldPx[j*width+i];
a = Color.alpha(color);
for(yy = j - n2;yy <= j + n2;yy++) {
mado[m] = Color.red(oldPx[yy*width+i]);
m++;
}
for(xx = i - n2; xx <= i + n2; xx++) {
if(xx == i)
continue;
mado[m] = Color.red(oldPx[j*width+xx]);
m++;
}
//把mado[m]中的值按下降顺序用冒泡排序
do {
chg = 0;
for(m = 0;m < nn - 1;m++) {
if(mado[m] < mado[m+1]) {
madom = mado[m];
mado[m] = mado[m+1];
mado[m+1] = madom;
chg = 1;
}
}
}while(chg == 1);
//求中值medi
medi = mado[chuo];
//把中值代入显示图像中
//Log.d("xyz","medi + " + medi);
newPx[j*width+i] = Color.argb(a,medi,medi,medi);
}
}
bmp.setPixels(newPx, 0, width, 0, 0, width, height);
mImageView.setImageBitmap(bmp);
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
下面是我们在接收用户输入时使用的对话框所引入的自定义布局threshold.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="阈值:"/>
<EditText
android:id="@+id/digit_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
效果
(三)N×N最大值滤波
思路
- 传入需要处理的图像
- 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
- 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
- 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
- 取得N值
- 循环,取得以某点为中心的N×N屏蔽窗口包括的各点像素值中的最大值
- 把该点灰度值置为最大值
- 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
- 创建一个高度、宽度和原图完全一样的新图
- 将存新数组中的颜色值赋给新图
- 将新图像返回
private void maxnn(final Bitmap bm) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("n*n最大值滤波");
builder.setMessage("请输入n值(大于3的奇数)");
View view1 = LayoutInflater.from(getContext()).inflate(R.layout.threshold,null);
final EditText mSingleThresholdEt = view1.findViewById(R.id.digit_dialog);
builder.setView(view1);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int k) {
Bitmap grayBitmap = twoGray(bm);
int width = bm.getWidth();
int height = bm.getHeight();
int color,gray,average;
int r, g, b, a = 0;
int yy,xx,n2 = 0,nn,chg = 0,m,medi,madom,madomax;
int[] mado = new int[1000];
Bitmap bmp = Bitmap.createBitmap(width, height
, Bitmap.Config.ARGB_8888);
int[] oldPx = new int[width * height];
int[] newPx = new int[width * height];
grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);
String str = mSingleThresholdEt.getText().toString();
if("".equals(str)) {
nm = 0;
} else {
nm = Integer.valueOf(str);
}
if(nm < 3 || nm % 2 != 1) {
Toast.makeText(getActivity(),"请输入一个大于等于3的奇数",Toast.LENGTH_SHORT).show();
return;
}
if(nm >= 3 && nm % 2 == 1) {
n2 = (nm - 1) / 2;
}
nn = nm*nm;
for(int j = n2;j < height - n2;j++) {
for(int i = n2;i < width - n2;i++) {
m = 0;
color = oldPx[j*width+i];
a = Color.alpha(color);
for(yy = j - n2;yy <= j + n2;yy++) {
for (xx = i - n2; xx <= i + n2; xx++) {
mado[m] = Color.red(oldPx[yy * width + xx]);
m++;
}
}
madomax = mado[0];
for(m = 1;m < nn;m++) {
if(madomax<mado[m])
madomax = mado[m];
}
newPx[j*width+i] = Color.argb(a,madomax,madomax,madomax);
}
}
bmp.setPixels(newPx, 0, width, 0, 0, width, height);
mImageView.setImageBitmap(bmp);
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
下面是我们在接收用户输入时使用的对话框所引入的自定义布局threshold.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="阈值:"/>
<EditText
android:id="@+id/digit_dialog"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
效果
代码已上传到github,点击这里可以下载体验