简介这份资源是华南农业大学毕业设计项目用Java实现一个类似美颜相机美图秀秀的Android应用面向计算机相关专业做毕业设计或课程设计的学生也适合想练手Android图像处理的开发者参考。压缩包共约2000个文件以1216个xml布局与资源文件、378个json配置、352个java源码为主另有少量sh脚本、properties配置和c语言文件整体约19.43MB目录结构完整便于按模块查阅。项目围绕相机预览、滤镜处理与图像美化展开从内容预览可见GPUImage滤镜框架、YUV解码等实现痕迹能帮助读者理解美颜类App的相机采集、滤镜链与渲染流程。已有118人学习可作为毕设选题的完整参考方案用于对照源码梳理功能模块、学习滤镜与图像处理思路并在此基础上做二次开发或答辩准备。1. 从一份毕业设计源码说起这个 Android 美颜相机到底能跑出什么第一次拿到这份源码时我其实没抱太大期待——毕业设计类的 Android 项目十个里有八个是「能点开、能拍照、滤镜一贴就完事」的壳子。但把这份类似美颜相机美图秀秀那类应用的工程解压、导入、跑起来之后我发现它的完成度比想象中高相机预览、拍照、本地相册选图、磨皮美白、滤镜叠加、贴纸、保存到相册这条主链路是通的而且代码分层还算清楚不是所有逻辑都堆在一个 Activity 里。对正在找 Java 毕业设计、课程设计参考的开发者来说它的价值不在于算法多先进而在于「一个完整的图像处理 App 该有哪些模块、这些模块怎么串起来」这件事被真实地写了一遍。下面我就按「它是什么 → 怎么跑起来 → 核心代码怎么写的 → 怎么改」的顺序把这份工程拆给你看。2. 工程结构与技术选型一个相机 App 的骨架长什么样2.1 目录分层与模块职责导入 Android Studio 之后先别急着点运行花五分钟把app/src/main/java下的包结构看一遍你对这个项目的理解会快很多。这类美颜相机工程的包名通常按功能切分而不是按类型把所有 Activity 塞一个包堆。典型的目录长这样com.example.beautycamera ├── ui/ 界面层主界面、相机页、编辑页、相册页 │ ├── MainActivity.java │ ├── CameraActivity.java │ ├── EditActivity.java │ └── GalleryActivity.java ├── camera/ 相机封装预览、对焦、拍照回调 │ └── CameraHelper.java ├── image/ 图像处理核心磨皮、美白、滤镜、贴纸合成 │ ├── BeautyFilter.java │ ├── FilterFactory.java │ └── StickerRenderer.java ├── adapter/ 列表适配器滤镜缩略图、贴纸列表、相册网格 ├── bean/ 数据模型图片项、滤镜项、贴纸项 ├── utils/ 工具类Bitmap 转换、文件存储、权限 │ ├── BitmapUtils.java │ ├── FileUtils.java │ └── PermissionUtils.java └── widget/ 自定义控件圆形按钮、滑动调节条这个分层的关键在于image/包和ui/包是解耦的界面层只负责拿参数、调方法、显示结果真正的像素运算全在image/里。你后面要改滤镜、加效果基本只动image/不用碰界面。这是判断一份毕设源码值不值得二次开发的第一条标准——如果磨皮代码直接写在onClick里那改起来就是灾难。2.2 技术栈与依赖选择工程是纯 Java 写的没有上 Kotlin这对课程设计场景反而友好大部分学校的教学语言还是 Java。相机部分用的是 Android 原生的CameraAPIandroid.hardware.Camera而不是 CameraX 或 Camera2。这一点要有心理准备原生CameraAPI 在 Android 5.0 之后就被标记为 deprecated但在毕业设计里它依然是最省事的选择因为 Camera2 的回调链复杂到能把人劝退CameraX 又需要额外引入 Jetpack 依赖。图像处理没有用 OpenGL ES 做 GPU 加速而是走BitmapCanvas 像素数组的 CPU 路线好处是代码直观、每行都能看懂坏处是处理大图会卡这个后面避坑章节会细说。依赖方面build.gradle里通常只有基础的 support/androidx 库可能加一个图片加载库Glide 或 Picasso用于相册缩略图。没有引入第三方美颜 SDK所有效果都是自己写的——这正是它作为学习素材的价值所在。// app/build.gradle 里常见的依赖配置 dependencies { implementation androidx.appcompat:appcompat:1.6.1 implementation androidx.constraintlayout:constraintlayout:2.1.4 // 图片加载用于相册网格缩略图 implementation com.github.bumptech.glide:glide:4.15.1 // 可能有的图片裁剪库视具体工程而定 implementation androidx.recyclerview:recyclerview:1.3.0 }这里要提醒一句compileSdkVersion和targetSdkVersion如果写的是很老的版本比如 28 以下在新版 Android Studio 上会提示升级但别急着无脑升到 34因为高版本对存储权限、后台相机、FileProvider的要求都变严了升完可能直接崩。稳妥做法是先按原配置跑通再逐步升。2.3 权限与清单配置相机 App 绕不开权限。AndroidManifest.xml里至少要声明相机、存储读写、录音如果录像这几项uses-permission android:nameandroid.permission.CAMERA / uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE android:maxSdkVersion28 / uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE / uses-feature android:nameandroid.hardware.camera android:requiredtrue /注意WRITE_EXTERNAL_STORAGE加了maxSdkVersion28这是 Android 10 分区存储之后的常见写法29 及以上用MediaStore写相册不再需要这个权限。如果你的工程里没有这行在 Android 11 真机上保存图片会失败这是新手最容易翻车的地方之一。运行时权限的申请逻辑一般在MainActivity或PermissionUtils里用ActivityCompat.requestPermissions走一遍即可。3. 跑起来从导入到拍出第一张美颜图的完整步骤3.1 环境准备与工程导入先把环境对齐不然报错会让你怀疑人生。推荐配置Android Studio近两年的版本都行、JDK 8 或 11看build.gradle里的sourceCompatibility、一台 Android 7.0 以上的真机。为什么强调真机因为模拟器的虚拟摄像头拍出来是测试图案你根本看不出磨皮和滤镜的效果而且部分模拟器对CameraAPI 支持不全预览直接黑屏。导入步骤File → Open选中解压后的工程根目录不是app目录等 Gradle Sync 完成。如果卡在下载依赖检查settings.gradle里的仓库地址是不是google()和mavenCentral()老工程可能还写着jcenter()而 jcenter 已经停服需要手动替换。// settings.gradle 里仓库配置的修正 dependencyResolutionManagement { repositories { google() mavenCentral() // 替换掉已停服的 jcenter() } }Sync 成功后如果报Manifest merger failed多半是minSdkVersion和某个库冲突把minSdkVersion提到 21 以上通常能解决。3.2 首次运行与相机预览验证点运行之前先确认CameraActivity是启动页或者在MainActivity里点「拍照」能跳过去。装到真机后第一次打开会弹权限框全部允许。如果预览画面是黑的按这个顺序排查一权限是否真的授予了去系统设置里看二SurfaceView或TextureView的surfaceCreated回调有没有触发打日志三Camera.open()有没有抛异常被其他 App 占用相机也会失败。预览正常之后点拍照看图片有没有落到EditActivity。这一步是整条链路的第一个关键节点通了说明相机封装和页面跳转没问题。常见现象是拍完闪退日志里是NullPointerException在Bitmap相关代码——大概率是拍照回调里data为 null用takePicture的PictureCallback时如果传了null给ShutterCallback或RawCallback某些机型行为不一致这个在避坑章节展开。3.3 编辑页效果验证与保存进入编辑页后依次验证磨皮、美白、滤镜、贴纸四个功能。磨皮和美白一般用滑动条调强度滤镜是底部缩略图列表贴纸是叠加在图上可拖动。每调一次观察画面是否实时刷新——如果卡顿超过一秒说明是 CPU 逐像素处理大图属于预期内的性能问题不是 bug。最后点保存去系统相册看图片在不在。保存失败是最常见的「最后一公里」问题原因通常是Android 10 以上没走MediaStore、FileProvider没配、或者路径写死到了/sdcard/根目录。下面这段是兼容性较好的保存写法// 保存 Bitmap 到系统相册兼容 Android 10 分区存储 public static void saveToGallery(Context context, Bitmap bitmap, String name) { ContentValues values new ContentValues(); values.put(MediaStore.Images.Media.DISPLAY_NAME, name .jpg); values.put(MediaStore.Images.Media.MIME_TYPE, image/jpeg); if (Build.VERSION.SDK_INT Build.VERSION_CODES.Q) { // Android 10 走 MediaStore无需 WRITE 权限 values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); Uri uri context.getContentResolver() .insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); try (OutputStream os context.getContentResolver().openOutputStream(uri)) { bitmap.compress(Bitmap.CompressFormat.JPEG, 95, os); } catch (IOException e) { e.printStackTrace(); } } else { // 低版本走传统文件写入 相册扫描 File dir Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); File file new File(dir, name .jpg); FileUtils.writeBitmap(file, bitmap); MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null); } }参数说明bitmap是编辑后的结果图name建议用时间戳避免重名RELATIVE_PATH指定存到 Pictures 目录。compress的质量参数 95 是经验值再高文件大、再低画质肉眼可见下降。这段代码直接抄进FileUtils就能用是这份工程里最值得复用的工具方法之一。4. 核心模块拆解磨皮、滤镜、贴纸三段代码怎么读4.1 磨皮与美白像素级处理的入门实现美颜的核心在BeautyFilter类。毕业设计级别的磨皮一般不会上双边滤波或导向滤波而是用「均值模糊 原图混合」的简化方案先对图像做一次模糊再按强度把模糊图和原图按比例叠加皮肤区域就被柔化了。美白则是整体提亮 轻微降低饱和度。看这段典型实现// 简化版磨皮均值模糊后与原图按强度混合 public static Bitmap smooth(Bitmap src, int radius, float strength) { int w src.getWidth(), h src.getHeight(); int[] pixels new int[w * h]; src.getPixels(pixels, 0, w, 0, 0, w, h); int[] blurred boxBlur(pixels, w, h, radius); // 均值模糊 int[] out new int[pixels.length]; for (int i 0; i pixels.length; i) { int a (pixels[i] 24) 0xff; int r mix((pixels[i] 16) 0xff, (blurred[i] 16) 0xff, strength); int g mix((pixels[i] 8) 0xff, (blurred[i] 8) 0xff, strength); int b mix(pixels[i] 0xff, blurred[i] 0xff, strength); out[i] (a 24) | (r 16) | (g 8) | b; } return Bitmap.createBitmap(out, w, h, Bitmap.Config.ARGB_8888); } // 按强度混合两个通道值strength 0~1 private static int mix(int orig, int blur, float strength) { return (int) (orig * (1 - strength) blur * strength); }逻辑说明boxBlur是均值模糊radius控制模糊半径半径越大皮肤越「糊」strength是滑动条映射的 0~1 强度0 是原图、1 是全模糊。mix做线性插值这是所有混合类效果的基础公式记住它后面滤镜叠加也是同一套思路。参数上radius一般取 3~8太大五官边缘会糊成一团strength默认给 0.5 左右比较自然。美白就是在out基础上对 RGB 各加一个偏移量并 clamp 到 255代码结构一样不重复贴。4.2 滤镜工厂用颜色矩阵统一管理多种风格滤镜如果每个都写一遍像素循环代码会爆炸。这份工程用的是颜色矩阵ColorMatrix方案一个矩阵描述一种色调变换Canvas.drawBitmap时套Paint.setColorFilter即可既简洁又能实时预览。FilterFactory里维护一个滤镜列表// 用 ColorMatrix 定义滤镜一个矩阵一种风格 public class FilterFactory { public static final float[] NONE { 1,0,0,0,0, 0,1,0,0,0, 0,0,1,0,0, 0,0,0,1,0 }; // 暖色红通道增益、蓝通道衰减 public static final float[] WARM { 1.1f,0,0,0,10, 0,1.0f,0,0,0, 0,0,0.9f,0,-10, 0,0,0,1,0 }; // 黑白三通道等权 public static final float[] MONO { 0.299f,0.587f,0.114f,0,0, 0.299f,0.587f,0.114f,0,0, 0.299f,0.587f,0.114f,0,0, 0,0,0,1,0 }; public static Bitmap apply(Bitmap src, float[] matrix) { Bitmap out Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas new Canvas(out); Paint paint new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColorFilter(new ColorMatrixColorFilter(matrix)); canvas.drawBitmap(src, 0, 0, paint); return out; } }逻辑说明颜色矩阵是 4x5 的浮点数组前四列是 RGBA 各通道的权重第五列是偏移量。WARM里红通道乘 1.1、蓝通道乘 0.9 并减 10整体就偏暖MONO用亮度公式把三通道拉平得到黑白。apply里用Canvas画一遍就完成变换比逐像素快得多因为底层有硬件加速。要加新滤镜只需在工厂里加一个矩阵常量再在缩略图列表里挂上去扩展成本极低——这就是「工厂 矩阵」设计的价值。4.3 贴纸渲染与手势拖动贴纸模块在StickerRenderer里核心是把贴纸 Bitmap 按用户手势的位移和缩放画到主图上。编辑页一般用一个自定义View承载onTouchEvent里处理ACTION_MOVE更新坐标ACTION_POINTER_DOWN处理双指缩放。最终合成时把主图和所有贴纸按顺序drawBitmap到同一张画布// 合成主图与贴纸stickers 里存了每张贴纸的 bitmap、坐标、缩放 public static Bitmap compose(Bitmap base, ListStickerItem stickers) { Bitmap out base.copy(Bitmap.Config.ARGB_8888, true); Canvas canvas new Canvas(out); for (StickerItem s : stickers) { canvas.save(); // 以贴纸中心为轴缩放避免缩放时位置漂移 canvas.scale(s.scale, s.scale, s.x s.bitmap.getWidth() / 2f, s.y s.bitmap.getHeight() / 2f); canvas.drawBitmap(s.bitmap, s.x, s.y, null); canvas.restore(); } return out; }逻辑说明save/restore保证每张贴纸的变换互不影响scale的轴心参数是关键如果直接用(0,0)做轴心贴纸一放大就往右下角跑这是新手写贴纸功能最常见的 bug。StickerItem里存x、y、scale三个字段手势回调里只改这三个值再invalidate()重绘合成时一次性画出来。参数上scale建议限制在 0.3~3.0防止缩没或缩爆。5. 避坑与排查真机上最容易翻车的五个点5.1 拍照后闪退日志指向 Bitmap 为空现象点拍照界面卡一下然后闪退Logcat 里是NullPointerException在Bitmap.createBitmap或copy附近。原因Camera.takePicture的PictureCallback在某些机型上data可能为 null尤其是同时传了ShutterCallback和RawCallback时部分厂商 ROM 行为不一致。解决在回调里先判空为空则回退到从SurfaceView抓帧或者干脆只传JPEG回调、其余传 null并在onPictureTaken里加if (data null) return;兜底。5.2 预览画面拉伸变形现象相机预览里人脸被拉长或压扁。原因SurfaceView的尺寸和相机预览尺寸Camera.Parameters.getPreviewSize()不匹配系统按控件比例硬拉伸。解决在surfaceChanged里根据控件宽高比从getSupportedPreviewSizes()里挑一个最接近的比例设置给相机再调整控件布局参数。这一步不做画面永远歪。5.3 保存图片成功但相册里看不到现象代码没报错文件也写了但系统相册刷不出来。原因Android 10 以下写文件后没有通知媒体库扫描Android 10 以上没走MediaStore而是直接写路径被分区存储拦截。解决低版本用MediaScannerConnection.scanFile主动扫描高版本按第 3.3 节的MediaStore写法插入。这是「文件存在但用户以为没保存」的经典误会。5.4 大图处理 OOM现象选一张 4000x3000 的照片进编辑页直接OutOfMemoryError。原因Bitmap按 ARGB_8888 算一张 1200 万像素图占约 48MB加上模糊副本、输出副本瞬间破百兆超过默认堆上限。解决进编辑页前先BitmapFactory.Options.inSampleSize降采样到屏幕尺寸的 2 倍以内处理完及时recycle()中间 BitmapAndroidManifest里加android:largeHeaptrue作为兜底但别把它当主要手段。5.5 滤镜切换卡顿、滑动条掉帧现象拖磨皮强度条时画面一顿一顿。原因每次onProgressChanged都全图重算主线程被占满。解决滑动过程中只做低分辨率预览把图缩到 1/4 处理再放大显示松手onStopTrackingTouch时再对原图做一次高质量处理或者把处理放到子线程用Handler回主线程刷新。这是性能优化的第一课也是答辩时最容易被问到的点。6. 二次开发把这份毕设改成能拿得出手的作品改造之前先想清楚方向。这份工程的骨架是「相机 编辑 保存」往上加东西有两条路一条是加功能人脸检测、贴纸跟随、美妆一条是提性能GPU 加速、异步处理。我一般建议先做后者因为性能是地基功能是装修地基不稳装修越多越容易塌。具体到代码最划算的一处改造是把BeautyFilter的 CPU 逐像素循环换成RenderScript或 OpenGL 着色器。以 RenderScript 为例把boxBlur换成ScriptIntrinsicBlur磨皮耗时能从几百毫秒降到几十毫秒代码量还更少// 用 RenderScript 内置模糊替代手写均值模糊性能提升明显 private static Bitmap rsBlur(Context ctx, Bitmap src, float radius) { Bitmap out Bitmap.createBitmap(src.getWidth(), src.getHeight(), Bitmap.Config.ARGB_8888); RenderScript rs RenderScript.create(ctx); ScriptIntrinsicBlur blur ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); Allocation in Allocation.createFromBitmap(rs, src); Allocation o Allocation.createFromBitmap(rs, out); blur.setRadius(Math.min(radius, 25f)); // 上限 25 blur.setInput(in); blur.forEach(o); o.copyTo(out); rs.destroy(); return out; }注意setRadius上限是 25超过会抛异常rs.destroy()必须调否则内存泄漏。改完用同一张图对比处理耗时用System.currentTimeMillis()前后夹一下就能验证别凭感觉说「快了」。第二条改造路线是加人脸检测让磨皮只作用于皮肤区域。用 Android 自带的FaceDetector虽然老但零依赖拿到人脸矩形把矩形外区域的strength设为 0磨皮就只糊脸不糊背景效果立刻上一个档次。验证方法很直接找一张背景有文字的照片改之前文字被糊掉改之后文字清晰、脸还是柔的就说明区域控制生效了。第三条是工程化把image/包抽成独立 module写几个单元测试固定住mix和颜色矩阵的输出值以后改滤镜就不怕改坏。这一步对毕设答辩加分不多但对「这份代码能不能继续用」意义很大。血泪经验是我见过太多人一上来就换 CameraX、上 OpenGL结果相机预览调不通、着色器编译报错最后连原本能跑的功能都回不去了。从那以后我每次动这类图像工程都强制先跑通原版、打一个 git tag再动第一行代码改一处验证一处。希望这份拆解能帮你少走几步弯路把这份源码真正跑成自己的东西。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?