Java编程实现高斯模糊和图像的空间卷积详解

高斯模糊

高斯模糊(英语:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等图像处理软件中广泛使用的处理效果,通常用它来减少图像杂讯以及降低细节层次。这种模糊技术生成的图像,其视觉效果就像是经过一个半透明屏幕在观察图像,这与镜头焦外成像效果散景以及普通照明阴影中的效果都明显不同。高斯平滑也用于计算机视觉算法中的预先处理阶段,以增强图像在不同比例大小下的图像效果。 从数学的角度来看,图像的高斯模糊过程就是图像与正态分布做卷积。由于正态分布又叫作高斯分布,所以这项技术就叫作高斯模糊。图像与圆形方框模糊做卷积将会生成更加精确的焦外成像效果。由于高斯函数的傅立叶变换是另外一个高斯函数,所以高斯模糊对于图像来说就是一个低通滤波器。

高斯模糊运用了高斯的正态分布的密度函数,计算图像中每个像素的变换。

根据一维高斯函数,可以推导得到二维高斯函数:

其中r是模糊半径,r^2 = x^2 + y^2,σ是正态分布的标准偏差。在二维空间中,这个公式生成的曲面的等高线是从中心开始呈正态分布的同心圆。分布不为零的像素组成的卷积矩阵与原始图像做变换。每个像素的值都是周围相邻像素值的加权平均。原始像素的值有最大的高斯分布值,所以有最大的权重,相邻像素随着距离原始像素越来越远,其权重也越来越小。这样进行模糊处理比其它的均衡模糊滤波器更高地保留了边缘效果。

其实,在iOS上实现高斯模糊是件很容易的事儿。早在iOS 5.0就有了Core Image的API,而且在CoreImage.framework库中,提供了大量的滤镜实现。

+(UIImage *)coreBlurImage:(UIImage *)image withBlurNumber:(CGFloat)blur 
{ 
  CIContext *context = [CIContext contextWithOptions:nil]; 
  CIImage *inputImage= [CIImage imageWithCGImage:image.CGImage]; 
  //设置filter
  CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"]; 
  [filter setValue:inputImage forKey:kCIInputImageKey]; [filter setValue:@(blur) forKey: @"inputRadius"]; 
  //模糊图片
  CIImage *result=[filter valueForKey:kCIOutputImageKey]; 
  CGImageRef outImage=[context createCGImage:result fromRect:[result extent]];    
  UIImage *blurImage=[UIImage imageWithCGImage:outImage];      
  CGImageRelease(outImage); 
  return blurImage;
}

在Android上实现高斯模糊也可以使用原生的API—–RenderScript,不过需要Android的API是17以上,也就是Android 4.2版本。

/**
   * 使用RenderScript实现高斯模糊的算法
   * @param bitmap
   * @return
   */
public Bitmap blur(Bitmap bitmap){
	//Let's create an empty bitmap with the same size of the bitmap we want to blur
	Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
	//Instantiate a new Renderscript
	RenderScript rs = RenderScript.create(getApplicationContext());
	//Create an Intrinsic Blur Script using the Renderscript
	ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
	//Create the Allocations (in/out) with the Renderscript and the in/out bitmaps
	Allocation allIn = Allocation.createFromBitmap(rs, bitmap);
	Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);
	//Set the radius of the blur: 0 < radius <= 25
	blurScript.setRadius(20.0f);
	//Perform the Renderscript
	blurScript.setInput(allIn);
	blurScript.forEach(allOut);
	//Copy the final bitmap created by the out Allocation to the outBitmap
	allOut.copyTo(outBitmap);
	//recycle the original bitmap
	bitmap.recycle();
	//After finishing everything, we destroy the Renderscript.
	rs.destroy();
	return outBitmap;
}

我们开发的图像框架cv4j也提供了一个滤镜来实现高斯模糊。

GaussianBlurFilter filter = new GaussianBlurFilter();
filter.setSigma(10);

RxImageData.bitmap(bitmap).addFilter(filter).into(image2);

可以看出,cv4j实现的高斯模糊跟RenderScript实现的效果一致。

其中,GaussianBlurFilter的代码如下:

public class GaussianBlurFilter implements CommonFilter {
	private float[] kernel;
	private double sigma = 2;
	ExecutorService mExecutor;
	CompletionService<Void> service;
	public GaussianBlurFilter() {
		kernel = new float[0];
	}
	public void setSigma(double a) {
		this.sigma = a;
	}
	@Override
	  public ImageProcessor filter(final ImageProcessor src){
		final int width = src.getWidth();
		final int height = src.getHeight();
		final int size = width*height;
		int dims = src.getChannels();
		makeGaussianKernel(sigma, 0.002, (int)Math.min(width, height));
		mExecutor = TaskUtils.newFixedThreadPool("cv4j",dims);
		service = new ExecutorCompletionService<>(mExecutor);
		// save result
		for (int i=0; i<dims; i++) {
			final int temp = i;
			service.submit(new Callable<Void>() {
				public Void call() throws Exception {
					byte[] inPixels = src.tobyte(temp);
					byte[] temp = new byte[size];
					blur(inPixels, temp, width, height);
					// H Gaussian
					blur(temp, inPixels, height, width);
					// V Gaussain
					return null;
				}
			}
			);
		}
		for (int i = 0; i < dims; i++) {
			try {
				service.take();
			}
			catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		mExecutor.shutdown();
		return src;
	}
	/**
   * <p> here is 1D Gaussian    , </p>
   *
   * @param inPixels
   * @param outPixels
   * @param width
   * @param height
   */
	private void blur(byte[] inPixels, byte[] outPixels, int width, int height)
	  {
		int subCol = 0;
		int index = 0, index2 = 0;
		float sum = 0;
		int k = kernel.length-1;
		for (int row=0; row<height; row++) {
			int c = 0;
			index = row;
			for (int col=0; col<width; col++) {
				sum = 0;
				for (int m = -k; m< kernel.length; m++) {
					subCol = col + m;
					if(subCol < 0 || subCol >= width) {
						subCol = 0;
					}
					index2 = row * width + subCol;
					c = inPixels[index2] & 0xff;
					sum += c * kernel[Math.abs(m)];
				}
				outPixels[index] = (byte)Tools.clamp(sum);
				index += height;
			}
		}
	}
	public void makeGaussianKernel(final double sigma, final double accuracy, int maxRadius) {
		int kRadius = (int)Math.ceil(sigma*Math.sqrt(-2*Math.log(accuracy)))+1;
		if (maxRadius < 50) maxRadius = 50;
		// too small maxRadius would result in inaccurate sum.
		if (kRadius > maxRadius) kRadius = maxRadius;
		kernel = new float[kRadius];
		for (int i=0; i<kRadius; i++)        // Gaussian function
		kernel[i] = (float)(Math.exp(-0.5*i*i/sigma/sigma));
		double sum;
		// sum over all kernel elements for normalization
		if (kRadius < maxRadius) {
			sum = kernel[0];
			for (int i=1; i<kRadius; i++)
			        sum += 2*kernel[i];
		} else
		      sum = sigma * Math.sqrt(2*Math.PI);
		for (int i=0; i<kRadius; i++) {
			double v = (kernel[i]/sum);
			kernel[i] = (float)v;
		}
		return;
	}
}

空间卷积

二维卷积在图像处理中会经常遇到,图像处理中用到的大多是二维卷积的离散形式。

以下是cv4j实现的各种卷积效果。

cv4j 目前支持如下的空间卷积滤镜

 

filter 名称 作用
ConvolutionHVFilter 卷积 模糊或者降噪
MinMaxFilter 最大最小值滤波 去噪声
SAPNoiseFilter 椒盐噪声 增加噪声
SharpFilter 锐化 增强
MedimaFilter 中值滤波 去噪声
LaplasFilter 拉普拉斯 提取边缘
FindEdgeFilter 寻找边缘 梯度提取
SobelFilter 梯度 获取x、y方向的梯度提取
VarianceFilter 方差滤波 高通滤波
MaerOperatorFilter 马尔操作 高通滤波
USMFilter USM 增强

 

cv4j 是gloomyfish和我一起开发的图像处理库,目前还处于早期的版本。

目前已经实现的功能:

这周,我们对 cv4j 做了较大的调整,对整体架构进行了优化。还加上了空间卷积功能(图片增强、锐化、模糊等等)。接下来,我们会做二值图像的分析(腐蚀、膨胀、开闭操作、轮廓提取等等)

总结

以上就是本文关于Java编程实现高斯模糊和图像的空间卷积详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:

70行Java代码实现深度神经网络算法分享

Java语言基于无向有权图实现克鲁斯卡尔算法代码示例

java算法实现红黑树完整代码示例

如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

一行java代码实现高斯模糊效果

本文实例为大家分享了本地图片或者网络图片高斯模糊效果(毛玻璃效果),具体内容如下

首先看效果图

1.本地图片高斯模糊

2.网络图片高斯模糊

github网址:https://github.com/qiushi123/BlurImageQcl

下面是使用步骤

一、实现本地图片或者网络图片的毛玻璃效果特别方便,只需要把下面的FastBlurUtil类复制到你的项目中就行

package com.testdemo.blur_image_lib10; 
 
import android.graphics.Bitmap; 
import android.graphics.BitmapFactory; 
 
 
import java.io.BufferedInputStream; 
import java.io.BufferedOutputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.net.URL; 
 
 
/** 
 * Created by qcl on 14/7/15. 
 */ 
public class FastBlurUtil { 
 /** 
  * 根据imagepath获取bitmap 
  */ 
 /** 
  * 得到本地或者网络上的bitmap url - 网络或者本地图片的绝对路径,比如: 
  * <p> 
  * A.网络路径: url="http://blog.foreverlove.us/girl2.png" ; 
  * <p> 
  * B.本地路径:url="file://mnt/sdcard/photo/image.png"; 
  * <p> 
  * C.支持的图片格式 ,png, jpg,bmp,gif等等 
  * 
  * @param url 
  * @return 
  */ 
 public static int IO_BUFFER_SIZE = 2 * 1024; 
 public static Bitmap GetUrlBitmap(String url, int scaleRatio) { 
  int blurRadius = 8;//通常设置为8就行。 
  if (scaleRatio <= 0) { 
   scaleRatio = 10; 
  } 
 
  Bitmap originBitmap = null; 
  InputStream in = null; 
  BufferedOutputStream out = null; 
  try { 
   in = new BufferedInputStream(new URL(url).openStream(), IO_BUFFER_SIZE); 
   final ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); 
   out = new BufferedOutputStream(dataStream, IO_BUFFER_SIZE); 
   copy(in, out); 
   out.flush(); 
   byte[] data = dataStream.toByteArray(); 
   originBitmap = BitmapFactory.decodeByteArray(data, 0, data.length); 
 
 
   Bitmap scaledBitmap = Bitmap.createScaledBitmap(originBitmap, 
     originBitmap.getWidth() / scaleRatio, 
     originBitmap.getHeight() / scaleRatio, 
     false); 
   Bitmap blurBitmap = doBlur(scaledBitmap, blurRadius, true); 
   return blurBitmap; 
  } catch (IOException e) { 
   e.printStackTrace(); 
   return null; 
  } 
 } 
 
 private static void copy(InputStream in, OutputStream out) 
   throws IOException { 
  byte[] b = new byte[IO_BUFFER_SIZE]; 
  int read; 
  while ((read = in.read(b)) != -1) { 
   out.write(b, 0, read); 
  } 
 } 
 
 // 把本地图片毛玻璃化 
 public static Bitmap toBlur(Bitmap originBitmap, int scaleRatio) { 
  //  int scaleRatio = 10; 
  // 增大scaleRatio缩放比,使用一样更小的bitmap去虚化可以到更好的得模糊效果,而且有利于占用内存的减小; 
  int blurRadius = 8;//通常设置为8就行。 
  //增大blurRadius,可以得到更高程度的虚化,不过会导致CPU更加intensive 
 
 
  /* 其中前三个参数很明显,其中宽高我们可以选择为原图尺寸的1/10; 
  第四个filter是指缩放的效果,filter为true则会得到一个边缘平滑的bitmap, 
  反之,则会得到边缘锯齿、pixelrelated的bitmap。 
  这里我们要对缩放的图片进行虚化,所以无所谓边缘效果,filter=false。*/ 
  if (scaleRatio <= 0) { 
   scaleRatio = 10; 
  } 
  Bitmap scaledBitmap = Bitmap.createScaledBitmap(originBitmap, 
    originBitmap.getWidth() / scaleRatio, 
    originBitmap.getHeight() / scaleRatio, 
    false); 
  Bitmap blurBitmap = doBlur(scaledBitmap, blurRadius, true); 
  return blurBitmap; 
 } 
 
 
 public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) { 
  Bitmap bitmap; 
  if (canReuseInBitmap) { 
   bitmap = sentBitmap; 
  } else { 
   bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); 
  } 
 
 
  if (radius < 1) { 
   return (null); 
  } 
 
 
  int w = bitmap.getWidth(); 
  int h = bitmap.getHeight(); 
 
 
  int[] pix = new int[w * h]; 
  bitmap.getPixels(pix, 0, w, 0, 0, w, h); 
 
 
  int wm = w - 1; 
  int hm = h - 1; 
  int wh = w * h; 
  int div = radius + radius + 1; 
 
 
  int r[] = new int[wh]; 
  int g[] = new int[wh]; 
  int b[] = new int[wh]; 
  int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; 
  int vmin[] = new int[Math.max(w, h)]; 
 
 
  int divsum = (div + 1) >> 1; 
  divsum *= divsum; 
  int dv[] = new int[256 * divsum]; 
  for (i = 0; i < 256 * divsum; i++) { 
   dv[i] = (i / divsum); 
  } 
 
 
  yw = yi = 0; 
 
 
  int[][] stack = new int[div][3]; 
  int stackpointer; 
  int stackstart; 
  int[] sir; 
  int rbs; 
  int r1 = radius + 1; 
  int routsum, goutsum, boutsum; 
  int rinsum, ginsum, binsum; 
 
 
  for (y = 0; y < h; y++) { 
   rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; 
   for (i = -radius; i <= radius; i++) { 
    p = pix[yi + Math.min(wm, Math.max(i, 0))]; 
    sir = stack[i + radius]; 
    sir[0] = (p & 0xff0000) >> 16; 
    sir[1] = (p & 0x00ff00) >> 8; 
    sir[2] = (p & 0x0000ff); 
    rbs = r1 - Math.abs(i); 
    rsum += sir[0] * rbs; 
    gsum += sir[1] * rbs; 
    bsum += sir[2] * rbs; 
    if (i > 0) { 
     rinsum += sir[0]; 
     ginsum += sir[1]; 
     binsum += sir[2]; 
    } else { 
     routsum += sir[0]; 
     goutsum += sir[1]; 
     boutsum += sir[2]; 
    } 
   } 
   stackpointer = radius; 
 
 
   for (x = 0; x < w; x++) { 
 
 
    r[yi] = dv[rsum]; 
    g[yi] = dv[gsum]; 
    b[yi] = dv[bsum]; 
 
 
    rsum -= routsum; 
    gsum -= goutsum; 
    bsum -= boutsum; 
 
 
    stackstart = stackpointer - radius + div; 
    sir = stack[stackstart % div]; 
 
 
    routsum -= sir[0]; 
    goutsum -= sir[1]; 
    boutsum -= sir[2]; 
 
 
    if (y == 0) { 
     vmin[x] = Math.min(x + radius + 1, wm); 
    } 
    p = pix[yw + vmin[x]]; 
 
 
    sir[0] = (p & 0xff0000) >> 16; 
    sir[1] = (p & 0x00ff00) >> 8; 
    sir[2] = (p & 0x0000ff); 
 
 
    rinsum += sir[0]; 
    ginsum += sir[1]; 
    binsum += sir[2]; 
 
 
    rsum += rinsum; 
    gsum += ginsum; 
    bsum += binsum; 
 
 
    stackpointer = (stackpointer + 1) % div; 
    sir = stack[(stackpointer) % div]; 
 
 
    routsum += sir[0]; 
    goutsum += sir[1]; 
    boutsum += sir[2]; 
 
 
    rinsum -= sir[0]; 
    ginsum -= sir[1]; 
    binsum -= sir[2]; 
 
 
    yi++; 
   } 
   yw += w; 
  } 
  for (x = 0; x < w; x++) { 
   rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; 
   yp = -radius * w; 
   for (i = -radius; i <= radius; i++) { 
    yi = Math.max(0, yp) + x; 
 
 
    sir = stack[i + radius]; 
 
 
    sir[0] = r[yi]; 
    sir[1] = g[yi]; 
    sir[2] = b[yi]; 
 
 
    rbs = r1 - Math.abs(i); 
 
 
    rsum += r[yi] * rbs; 
    gsum += g[yi] * rbs; 
    bsum += b[yi] * rbs; 
 
 
    if (i > 0) { 
     rinsum += sir[0]; 
     ginsum += sir[1]; 
     binsum += sir[2]; 
    } else { 
     routsum += sir[0]; 
     goutsum += sir[1]; 
     boutsum += sir[2]; 
    } 
 
 
    if (i < hm) { 
     yp += w; 
    } 
   } 
   yi = x; 
   stackpointer = radius; 
   for (y = 0; y < h; y++) { 
    // Preserve alpha channel: ( 0xff000000 & pix[yi] ) 
    pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; 
 
 
    rsum -= routsum; 
    gsum -= goutsum; 
    bsum -= boutsum; 
 
 
    stackstart = stackpointer - radius + div; 
    sir = stack[stackstart % div]; 
 
 
    routsum -= sir[0]; 
    goutsum -= sir[1]; 
    boutsum -= sir[2]; 
 
 
    if (x == 0) { 
     vmin[y] = Math.min(y + r1, hm) * w; 
    } 
    p = x + vmin[y]; 
 
 
    sir[0] = r[p]; 
    sir[1] = g[p]; 
    sir[2] = b[p]; 
 
 
    rinsum += sir[0]; 
    ginsum += sir[1]; 
    binsum += sir[2]; 
 
 
    rsum += rinsum; 
    gsum += ginsum; 
    bsum += binsum; 
 
 
    stackpointer = (stackpointer + 1) % div; 
    sir = stack[stackpointer]; 
 
 
    routsum += sir[0]; 
    goutsum += sir[1]; 
    boutsum += sir[2]; 
 
 
    rinsum -= sir[0]; 
    ginsum -= sir[1]; 
    binsum -= sir[2]; 
 
 
    yi += w; 
   } 
  } 
 
 
  bitmap.setPixels(pix, 0, w, 0, 0, w, h); 
 
 
  return (bitmap); 
 } 
 
 
} 

二、使用实例

package com.testdemo; 
 
 import android.app.Activity; 
 import android.content.res.Resources; 
 import android.graphics.Bitmap; 
 import android.graphics.BitmapFactory; 
 import android.os.Bundle; 
 import android.text.TextUtils; 
 import android.view.View; 
 import android.widget.EditText; 
 import android.widget.ImageView; 
 
 import com.testdemo.blur_image_lib10.FastBlurUtil; 
 
 public class MainActivity10_BlurImage extends Activity { 
  ImageView image; 
  EditText edit; 
 
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
   super.onCreate(savedInstanceState); 
   setContentView(R.layout.activity_main10_blur_image); 
   image = (ImageView) findViewById(R.id.image); 
   edit = (EditText) findViewById(R.id.edit); 
 
 
   findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View v) { 
     String pattern = edit.getText().toString(); 
     int scaleRatio = 0; 
     if (TextUtils.isEmpty(pattern)) { 
      scaleRatio = 0; 
     } else if (scaleRatio < 0) { 
      scaleRatio = 10; 
     } else { 
      scaleRatio = Integer.parseInt(pattern); 
     } 
 
     //  获取需要被模糊的原图bitmap 
     Resources res = getResources(); 
     Bitmap scaledBitmap = BitmapFactory.decodeResource(res, R.drawable.filter); 
 
     //  scaledBitmap为目标图像,10是缩放的倍数(越大模糊效果越高) 
     Bitmap blurBitmap = FastBlurUtil.toBlur(scaledBitmap, scaleRatio); 
     image.setScaleType(ImageView.ScaleType.CENTER_CROP); 
     image.setImageBitmap(blurBitmap); 
    } 
   }); 
 
   findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View v) { 
     //url为网络图片的url,10 是缩放的倍数(越大模糊效果越高) 
     final String pattern = edit.getText().toString(); 
 
     final String url = 
       //  "http://imgs.duwu.me/duwu/doc/cover/201601/18/173040803962.jpg"; 
       "http://b.hiphotos.baidu.com/album/pic/item/caef76094b36acafe72d0e667cd98d1000e99c5f.jpg?psign=e72d0e667cd98d1001e93901213fb80e7aec54e737d1b867"; 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       int scaleRatio = 0; 
       if (TextUtils.isEmpty(pattern)) { 
        scaleRatio = 0; 
       } else if (scaleRatio < 0) { 
        scaleRatio = 10; 
       } else { 
        scaleRatio = Integer.parseInt(pattern); 
       } 
 //      下面的这个方法必须在子线程中执行 
       final Bitmap blurBitmap2 = FastBlurUtil.GetUrlBitmap(url, scaleRatio); 
        
 //      刷新ui必须在主线程中执行 
        APP.runOnUIThread(new Runnable() {//这个是我自己封装的在主线程中刷新ui的方法。 
        @Override 
        public void run() { 
         image.setScaleType(ImageView.ScaleType.CENTER_CROP); 
         image.setImageBitmap(blurBitmap2); 
 
        } 
       }); 
      } 
     }).start(); 
 
 
    } 
   }); 
 
 
  } 
 
 }  

下面是上面的布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 xmlns:tools="http://schemas.android.com/tools" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:orientation="vertical"> 
 
 
<ImageView 
android:id="@+id/image2" 
android:layout_width="match_parent" 
android:layout_height="220dp" 
android:background="@drawable/filter"/> 
 
 
<LinearLayout 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:orientation="horizontal"> 
 
 
<EditText 
android:id="@+id/edit" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:layout_marginTop="15dp" 
android:hint="输入模糊度" 
/> 
 
 
<Button 
android:id="@+id/button2" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:text="转化毛玻璃"/> 
 
 
<Button 
android:id="@+id/button" 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:layout_marginLeft="4dp" 
android:text="转化网络图片毛玻璃"/> 
</LinearLayout> 
 
 
<ImageView 
android:id="@+id/image" 
android:layout_width="match_parent" 
android:layout_height="220dp" 
android:layout_below="@+id/image2" 
/> 
</LinearLayout> 

三、注意事项

1.一定不要忘记intent权限
2.加载网络图片时一定要在子线程中执行。

github网址:https://github.com/qiushi123/BlurImageQcl

以上就是本文的全部内容,希望对大家的学习有所帮助,