图片高斯模糊处理应用
发表时间:2020-10-19
发布人:葵宇科技
浏览次数:41
转载请保存原文链接: http://write.blog.csdn.net/postedit/42400825
当今微博、同伙圈、各类社交平台上充斥着各类照片,自拍或者各类生活记录也多以照片为其重要的手段,可见照片对生活的重要性,所以图片处理软件几乎成为人们的必备软件之一。
因为人们对图片处理的需求较大年夜,我欲望能开辟一款在生活中能应用的手机图片处理软件,于是我拔取了一个应用处景,即人们在各类场合拍┞氛时,只欲望照片显示的重点是本身存眷的内容,而非其他事物,但照片不免包含其他不肯意让别人看到的、可有可无的器械:比如小我隐私,路人等。
所以本应用软件出生了,该应用软件是一款运行于android体系上的应用软件,其重要功能是让用户拔取一个可调节大年夜小的圆形区域,并模糊掉落圆形区域外的内容。
设计分析思路
拔取应用技巧:
Java是andorid应用软件的重要开辟说话,应用java可以异常高效的开辟应用
NDK技巧:因为手机上的内存较少,处理器运算才能较弱,而应用Java对内存的┞菲握较弱,处理效力也不高,但图片资本将占据较大年夜的内存空间,并且处理照片须要宏大年夜的运算,所以应用NDK技巧,一种基于JNI技巧(使得java可以或许调用其他说话所编写的法度榜样,如C/C++),并且包含很多处理对象的技巧。
关键技巧分析:
根据功能:1、解析图片2、展示图片3、拔取圆区域覆盖的图片,4、模糊图片像素,
1、不合格式的图片,重要指的是图片数据的存储方法不合,该软件今朝拔取的图片格式为JPEG格式,应用LibJPEG开源库解析和生成jpeg图片
2、 展示图片:
因为图片大年夜小和比例和显示区域大年夜小和比例难以完全像同,所认为了正常显示图片,并且节俭内存,应用插值法拔取图片内容来显示。
3、 拔取圆区域覆盖的图片:
设圆心为x0,y0,半径为r,某一点的坐标为(x,y),则可得如下式子:
(x-x0)2+(y-y0)2 <= r2时, 该坐标为(x,y)是圆内的点
4、 模糊图片像素
模糊的算法有很多,并且不合算法有特定的应用处合,根据应用功能:拔取了均值模糊和高斯模糊算法,均值模糊实现简单,但模糊后的图片持续性不强,显示效不雅不好,所以拔取了高斯模糊算法
法度榜样简单流程图
[img]http://img.blog.csdn.net/20150104212620782?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMDU5MzY4MA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
各模块(类)的功能及法度榜样解释;
选择图片部分,调用手机其他法度榜样以获取图片路径:
Uri originalUri = data.getData();
String[] proj = {MediaStore.Images.Media.DATA };
// 似乎是android多媒体数据库的封装接口,具体的看Android文档
Cursorcursor = managedQuery(originalUri, proj, null, null,
null);
// 按我小我懂得 这个是获得用户选择的图片的索引值
int column_index =cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
// 将光标移至开首 ,这个很重要,不当心很轻易引起越界
cursor.moveToFirst();
// 最后根据索引值获取图片路径
String path =cursor.getString(column_index);
经由过程后缀名,初步断定是否是JPEG图片,如不雅后缀为.jpg则打开模糊设置界面:
if (path != null && path.endsWith(".jpg")) {
ImageShare.imgPath= path;
startActivity= CIRCLE_BLUZ;
startActivity(intent);
} else {
startActivity= 0;
ToastUtil.show(this,"请选择jpeg图片");
}
本处所法:
//展示原始图片,并获取生成缩略图
publicnative void showImg(Surface s, String imgPath);
//展示原图缩略图
public native void showCurImg(Surface s);
//模糊原始图片,并保存
public native void circleBluz(StringsavePath, int x, int y, int radius,
float sigma);
//展示模糊效不雅,主如果处理缩略图,并显示
public native void circleBluzShow(Surfaces, int x, int y, int radius,
float sigma);
//烧毁所有本地数据
public native void destroyNativeAll();
关键模块解释
拔取圆形区域,为了进步效力进行了优化
第一步:在圆的四周画一个半径为2r的┞俘方形,正方形的面积为4r2,
第二步:在圆内画一个半径为√ ̄2的┞俘方形,面积为2r2
第三步:断定在第一步的┞俘方形中又不在第二步的┞俘方形中的像素是否在圆内
优化效不雅为:削减了一半的像素(4r2,- 2r2)进行是否在圆内的断定
int powr2 = r * r; //待检区域 int checkl, checkt, checkr, checkb; checkl = x0 - r > 0 ? x0 - r : 0; checkt = y0 - r > 0 ? y0 - r : 0; checkr = x0 + r >= width ? width : x0+ r; checkb = y0 + r >= height ? height :y0 + r; //免检区域 int notcl, notct, notcr, notcb; //sqrt(2)/2 float sqrt2half = 0.7071; int nr = sqrt2half * r; notcl = x0 - nr > 0 ? x0 - nr : 0; notct = y0 - nr > 0 ? y0 - nr : 0; notcr = x0 + nr >= width ? width - 1 :x0 + nr; notcb = y0 + nr >= height ? height - 1: y0 + nr; int kcenter = ksize / 2; int i = 0, j = 0; unsigned long sum; //x偏向一维高斯模糊 for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++){ if (y >= checkt&& y <= checkb && x >= checkl && x <= checkr){ //断定是否在免检区域内 if (y >= notct&& y <= notcb && x >= notcl && x <= notcr) { } else { //断定是否在圆内 if(pow2minus(y, y0) + pow2minus(x, x0) <= powr2) { //在圆内 } else { //不在圆内 //进行模糊处理,省略 } } } else {//必定不在园内的像素 //进行模糊处理,省略 } } } } }接下来的是最重要的模糊模块:
模糊算法有很多 种,有最简单的均值模糊,这种模糊对细节的保存不敷,图片模糊后显得很僵硬,不美不雅,不相符请求,就直接舍弃了,接下来选择了高斯模糊,这种算法的思惟是离模糊中间像素越近的元素对该模糊后的像素影响越大年夜,所以应占据更大年夜的比重,越远的像素占据的比重越小
请先浏览:http://blog.csdn.net/zddblog/article/details/7450033说得很具体
这里重要 应用了以上博客的算法,并作了优化。用σ表示模糊程度,在大年夜概3σ距离之外的像素都可以看作不起感化,这些像素的计算也就可以忽视。平日,图像处理法度榜样只须要计算(6σ+1)*(6σ+1)的矩阵就可以包管相干像素影响。如下图是σ=0.3时获得的高斯模板矩阵,用这个矩阵来乘以中心像素四周的像素就可以获得中心像素的实际值。(如计算模糊后的像素(i,j),则其值为(i-1,j-1)*1.47169e -005+(i-1,j)*0.00380683+....+(i+1,j+1)*1.47169e -005)
[img]http://img.blog.csdn.net/20150104215617812?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMDU5MzY4MA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
高斯模糊第一步都是求高斯模糊的核心矩阵,接下来再用这个矩阵来计算每一个像素的实际值
//获得高斯核矩阵 void GaussianSmooth2D(double sigma, double ** & kernel, int& ksize) { //确保sigma为正数 sigma = sigma > 0 ? sigma : 0; //高斯核矩阵的大年夜小为(6*sigma+1)*(6*sigma+1) ksize = ceil(sigma * 3) * 2 + 1; //计算高斯核矩阵 //double *kernel = new double[ksize*ksize]; kernel = (double **) malloc(sizeof(double*) * ksize); for (int i = 0; i < ksize; i++) { kernel[i] = (double *) malloc(sizeof(double) * ksize); memset(kernel[i], 0, ksize); } double scale = -0.5 / (sigma * sigma); //-1/2sigma的平方 const double PI = 3.141592653; double cons = -scale / PI; double sum = 0.0; for (int i = 0; i < ksize; i++) { for (int j = 0; j < ksize; j++) { int x = i - (ksize - 1) / 2; int y = j - (ksize - 1) / 2; kernel[i][j] = cons * exp(scale * (x * x + y * y)); sum += kernel[i][j]; } } //归一化 for (int i = 0; i < ksize; i++) { for (int j = 0; j < ksize; j++) { kernel[i][j] /= sum; } } }接下来将膳绫擎两处代数归并应用,对圆外的像素进行模糊
//输入两个图片模板的模糊函数 //圆型模糊图片 int circleFuzzyDouble(unsigned char ** img, unsigned char ** imgtmp, int width, int height, int component, int x0, int y0, int r) { LOGI("circleFuzzy "); //高斯核矩阵 double ** kernel; double sigma = 10.0; int ksize; GaussianSmooth2D(sigma, kernel, ksize); LOGI("获得高斯矩阵 "); //3处理函数 int powr2 = r * r; //待检区域 int checkl, checkt, checkr, checkb; checkl = x0 - r > 0 ? x0 - r : 0; checkt = y0 - r > 0 ? y0 - r : 0; checkr = x0 + r >= width ? width : x0 + r; checkb = y0 + r >= height ? height : y0 + r; //免检区域 int notcl, notct, notcr, notcb; //sqrt(2)/2 float sqrt2half = 0.7071; int nr = sqrt2half * r; notcl = x0 - nr > 0 ? x0 - nr : 0; notct = y0 - nr > 0 ? y0 - nr : 0; notcr = x0 + nr >= width ? width - 1 : x0 + nr; notcb = y0 + nr >= height ? height - 1 : y0 + nr; //i控制行 纰谬边沿进行处理 for (int i = ksize; i < height - ksize; i++) { //j控制列 纰谬边沿进行处理 for (int j = ksize; j < width - ksize; j++) { //断定是否在待检查区域内 if (i >= checkt && i <= checkb && j >= checkl && j <= checkr) { //断定是否在免检区域内 if (i >= notct && i <= notcb && j >= notcl && j <= notcr) { } else { //断定是否在圆内 if (pow2minus(i, y0) + pow2minus(j, x0) <= powr2) { } else { //不在圆内 用高斯核矩阵进行模糊 double middle[3] = { 0, 0, 0 }; int mkl = j - ksize / 2; int mkt = i - ksize / 2; for (int k = 0; k < ksize; k++) { for (int l = 0; l < ksize; l++) { middle[0] += imgtmp[mkt + k][3 * (mkl + l)] * kernel[k][l]; middle[1] += imgtmp[mkt + k][3 * (mkl + l) + 1] * kernel[k][l]; middle[2] += imgtmp[mkt + k][3 * (mkl + l) + 2] * kernel[k][l]; } } img[i][3 * j] = (unsigned char) middle[0]; img[i][3 * j + 1] = (unsigned char) middle[1]; img[i][3 * j + 2] = (unsigned char) middle[2]; } } } else { //绝对不在圆内,对其进行模糊处理 double middle[3] = { 0, 0, 0 }; int mkl = j - ksize / 2; int mkt = i - ksize / 2; for (int k = 0; k < ksize; k++) { for (int l = 0; l < ksize; l++) { middle[0] += imgtmp[mkt + k][3 * (mkl + l)] * kernel[k][l]; middle[1] += imgtmp[mkt + k][3 * (mkl + l) + 1] * kernel[k][l]; middle[2] += imgtmp[mkt + k][3 * (mkl + l) + 2] * kernel[k][l]; } } img[i][3 * j] = (unsigned char) middle[0]; img[i][3 * j + 1] = (unsigned char) middle[1]; img[i][3 * j + 2] = (unsigned char) middle[2]; } } } LOGI("circleFuzzy end "); return 0; }
以上代码是没有进行边沿的像素处理的,可以把四周的像素也进行进行部分高斯核矩阵的乘法运算,但必定有部分矩阵的元素没有对应的像素进行乘法,这时,必须对应用高斯模糊矩阵元素进行归一化,不然元素明显值变小,此处代码大年夜家本身实现吧!
如许就能初步实现模糊的效不雅了,歇息一下!!看下实现的效不雅吧
[img]http://img.blog.csdn.net/20150104220951359?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMDU5MzY4MA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
点击√显示效不雅
[img]http://img.blog.csdn.net/20150104220940121?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMDU5MzY4MA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
然则膳绫擎的算法边沿处理异常麻烦,运行耗时也异常大年夜,为了进一步晋升速度,可以将膳绫擎的二维高斯函数,分别成两次一维的高斯运算,一次对X偏向上,另一次对Y偏向上,膳绫擎的博客说得比较清跋扈了,进行这种变换,速度变为本来的2倍以上,且不消受边沿处理的懊末路。
//浮点型的一维高斯核矩阵 <span style="white-space:pre"> </span>double * kdouble = (double *) malloc(sizeof(double) * ksize); double scale = -0.5 / (sigma * sigma); const double PI = 3.141592653; double cons = 1 / sqrt(-scale / PI); double sum = 0; int kcenter = ksize / 2; int i = 0, j = 0; for (i = 0; i < ksize; i++) { int x = i - kcenter; *(kdouble + i) = cons * exp(x * x * scale); //一维高斯函数 sum += *(kdouble + i); } //归一化,确保高斯权值袈溱[0,1]之间 for (i = 0; i < ksize; i++) { *(kdouble + i) /= sum; }
接下来,我们在推敲运行情况,我们的应用运行在手机上,手机的处理器进行浮点运算的才能很弱,特别是该算法多次应用了double型变量,并且每个圆外像素均须要次数较大年夜的浮点运算,如不雅我们能将浮点运算转化为整形运算,那么速度将大年夜幅晋升,而这是可以做到,并且比较简单:可以用先把double型的kernel放大年夜必定倍数,并保存为unsigned long型,今后就和本来运算一般,最后将求得的结不雅在缩小必定倍数即可。
//获得一维unsigned long高斯 ,放大年夜0xffffff void GaussianKerneluLong(unsigned long * & kernel, int sigma, int& ksize) { sigma = sigma > 0 ? sigma : -sigma; //高斯核矩阵的大年夜小为(6*sigma+1)*(6*sigma+1) //ksize为奇数 unsigned long ratio = 0xffffff; ksize = ceil(sigma * 3) * 2 + 1; //浮点型的一维高斯核矩阵 double * kdouble = (double *) malloc(sizeof(double) * ksize); //计算一维高斯核 kernel = (unsigned long *) malloc(sizeof(unsigned long) * ksize); double scale = -0.5 / (sigma * sigma); const double PI = 3.141592653; double cons = 1 / sqrt(-scale / PI); double sum = 0; int kcenter = ksize / 2; int i = 0, j = 0; for (i = 0; i < ksize; i++) { int x = i - kcenter; *(kdouble + i) = cons * exp(x * x * scale); //一维高斯函数 sum += *(kdouble + i); } //归一化,确保高斯权值袈溱[0,1]之间 for (i = 0; i < ksize; i++) { *(kdouble + i) /= sum; } //将double型矩阵转化为long型(放大年夜ratio倍) for (i = 0; i < ksize; i++) { kernel[i] = kdouble[i] * ratio; } //释放double型核矩阵 free(kdouble); }如斯一来,优化效不雅明显,速度晋升为本来的8~9倍
接下来持续优化的偏向是应用Neon和多线程
应用多线程优化,可以将待模糊矩阵,分为手机处理器的核数个面积相等的矩阵,在进行模糊处理,速度可以或许晋升约等于核心数倍
该部分优化将在后续更新,也迎接大年夜家给出更好的看法~ ~
感激大年夜家抽空看完这边博客,如不雅认为对您有一些赞助,或者有一些疑问,可以鄙人方留言,迎接大年夜家交换评论辩论。
应用的apk文件在这里,源代码在这里,建议先安装apk试用功能。