# pic_edit **Repository Path**: awol2010ex/pic_edit ## Basic Information - **Project Name**: pic_edit - **Description**: Android 图片/视频编辑工具,基于 OpenCV 实现 AI 智能消除、智能去水印、批量格式转换,以及视频裁剪与去水印。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-03 - **Last Updated**: 2026-07-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # PicEdit Android 图片/视频编辑工具,基于 OpenCV 实现 AI 智能消除、智能去水印、批量格式转换,以及视频裁剪与去水印。 ## 功能 - **AI 智能消除** — 单指涂抹要消除的区域,OpenCV Telea 算法自动填充修复 - **智能去水印** — 自适应阈值 + 轮廓分析自动检测水印位置并去除 - **批量去水印** — 逐图智能检测水印(有则处理、无则跳过),结果卡片可点击弹出大图对比 - **批量格式转换** — JPEG / PNG / WebP 互转,支持质量调节,结果卡片点击可切换原图/处理后对比 - **批量裁剪** — 按百分比裁剪图片边缘(顶部、底部、左边、右边),支持预览裁剪效果,结果卡片可切换原图/裁剪后对比 - **排版打印** — 将多张图片(1~9张)按网格/横排/竖排等 9 种排版方式合成一张大图,适合打印输出(PNG,每个格子 1200×1200px) - **视频编辑** — 时间裁剪(逐帧步进 + RangeSlider)、空间裁剪、区域去水印(逐帧 OpenCV Telea inpaint)、预览处理效果,导出 HEVC 视频 - **双指缩放画布** — 涂抹时双指缩放平移,支持精细定位 - **画笔粗细可调** — 滑块控制笔触大小(4–80dp) - **自建图库选择器** — 替代系统 Photo Picker,支持相册切换、单选/多选、全选功能,Coil 视频首帧缩略图 - **多步撤销** — history 栈保存每次处理前状态,可任意回退 - **原地编辑** — 处理结果直接替换画布,无中间结果页 - **对比预览** — 处理后点击右上角切换查看原图/处理后,不满意可一键回退 - **保存到原图目录** — 处理后自动保存到原图所在目录(DCIM/Pictures),非允许目录(如 Download)自动回退到 Pictures/,文件名保持不变(后缀改为目标格式) - **自建图库选择器** — 替代系统 Photo Picker,支持相册切换、单选/多选、全选功能,Coil 视频首帧缩略图 ## 技术栈 | 层面 | 技术 | |------|------| | 语言 | Kotlin 1.9.22 | | UI | Jetpack Compose 1.6.2 + Material 3 1.2.0 | | 架构 | MVVM (ViewModel + StateFlow) | | 导航 | Navigation Compose 2.7.7 | | 图像处理 | OpenCV 4.9.0 (`Photo.inpaint()` Telea 算法) | | 水印检测 | 自适应阈值 + 形态学闭运算 + 轮廓过滤 | | 视频处理 | Media3 ExoPlayer + Transformer (逐帧 OpenCV inpaint) | | 图片加载 | Coil Compose 2.6.0 + coil-video 2.6.0 | | 构建 | Gradle 8.5 + AGP 8.2.2 + Kotlin 1.9.22 | | 最低 API | 35 (Android 15) | | JDK | JDK 21 | ## 前置条件 - JDK 21+ - Android SDK 35 - 连接 Android 设备(USB 或 WiFi ADB) ## 快速开始 ```bash # 完整构建 ./gradlew build # 安装 debug 包(需连接设备) ./gradlew installDebug # Release 包 ./gradlew assembleRelease ``` ## 项目结构 ``` pic_edit/ ├── app/ │ └── src/main/java/com/picedit/app/ │ ├── ai/ # 图像处理 & 水印检测 & 视频特效 │ │ ├── InpaintingEngine.kt # 涂抹消除接口 │ │ ├── OpenCvInpaintingEngine.kt # OpenCV Telea 实现 │ │ ├── WatermarkDetector.kt # 水印自动检测 │ │ └── VideoInpaintEffect.kt # 视频逐帧去水印 (GlEffect) │ ├── navigation/ # NavGraph, Routes │ │ └── NavGraph.kt │ ├── ui/ │ │ ├── screen/ # 页面(一个文件一个页面) │ │ │ ├── HomeScreen.kt # 首页(功能入口) │ │ │ ├── EditScreen.kt # 单图编辑(涂抹/去水印) │ │ │ ├── BatchWatermarkScreen.kt # 批量去水印 │ │ │ ├── BatchConvertScreen.kt # 批量格式转换 │ │ │ ├── BatchCropScreen.kt # 批量裁剪 │ │ │ ├── LayoutPrintScreen.kt # 排版打印 │ │ │ ├── VideoEditScreen.kt # 视频编辑(裁剪/去水印/导出) │ │ │ └── GalleryScreen.kt # 自建图库选择器(相册/多选) │ │ ├── component/ # 可复用 UI 组件 │ │ │ ├── DrawingCanvas.kt # 涂抹画布(手势 + mask 渲染) │ │ │ ├── ComparisonDialog.kt # 原图/处理后对比弹窗 │ │ │ └── PermissionWrapper.kt # Android 13+ 媒体权限请求 │ │ └── theme/ # Material 3 主题 │ │ └── Theme.kt │ ├── viewmodel/ # MVVM ViewModel + UI State │ │ ├── EditViewModel.kt │ │ ├── BatchWatermarkViewModel.kt │ │ ├── BatchConvertViewModel.kt │ │ ├── BatchCropViewModel.kt │ │ ├── LayoutPrintViewModel.kt # 排版合成 │ │ ├── VideoEditViewModel.kt │ │ └── GalleryViewModel.kt # 自建图库选择器 ViewModel │ └── util/ # 纯工具类 │ ├── AppConfig.kt # 应用配置(MAX_IMAGE_DIMENSION) │ ├── BitmapUtils.kt # 图片加载、缩放、保存 │ ├── VideoUtils.kt # 视频信息读取、保存 │ └── MediaStoreRepository.kt # MediaStore 媒体查询(相册/文件) ├── gradle/ │ ├── libs.versions.toml # 版本目录(统一管理全部依赖版本) │ └── wrapper/ # Gradle Wrapper ├── app/build.gradle.kts # 应用构建脚本 ├── build.gradle.kts # 根构建脚本 ├── settings.gradle.kts # 项目设置 ├── gradle.properties # Gradle 配置(JDK 路径等) ├── AGENTS.md # AI 开发规范 └── .env # 应用配置(MAX_IMAGE_DIMENSION) ``` ## 配置 通过项目根目录 `.env` 文件配置: ```env MAX_IMAGE_DIMENSION=2048 ``` Gradle JDK 在 `gradle.properties` 中配置: ```properties org.gradle.java.home=D\:/Java/jdk-21.0.2 ``` ## 架构概览 ``` ┌──────────────────────────────────────────────────┐ │ UI Layer │ │ HomeScreen → EditScreen / Batch* / LayoutPrint / VideoEdit / Gallery │ │ DrawingCanvas (手势采集 + mask 渲染) │ │ ComparisonDialog (原图/结果对比) │ ├──────────────────────────────────────────────────┤ │ ViewModel Layer │ │ EditViewModel / BatchWatermarkViewModel │ │ BatchConvertViewModel / BatchCropViewModel │ │ LayoutPrintViewModel │ │ VideoEditViewModel │ │ StateFlow 驱动 UI,协程处理耗时任务 │ ├──────────────────────────────────────────────────┤ │ AI / Util Layer │ │ InpaintingEngine (接口) │ │ OpenCvInpaintingEngine (OpenCV Telea 实现) │ │ WatermarkDetector (自适应阈值 + 形态学) │ │ VideoInpaintEffect (Media3 GlEffect 逐帧处理) │ │ BitmapUtils / VideoUtils / AppConfig │ └──────────────────────────────────────────────────┘ ``` ## 编辑工作流 ### 单图编辑 1. 用户选图 → `loadImage()` 缩放至 `MAX_IMAGE_DIMENSION`(默认 2048px) 2. **手动涂抹** → `DrawingCanvas` 收集触摸点(bitmap 像素坐标)→ 生成 mask Bitmap 3. **智能水印检测** → `WatermarkDetector.detectMask()` → 自适应阈值 + 轮廓分析 → mask Bitmap 4. 处理 → `InpaintingEngine.inpaint(original, mask)` → 返回结果 Bitmap(原地替换 canvas,不跳结果页) 5. 保存 → `BitmapUtils.saveBitmapToPublic()` → MediaStore 写入公共图库(原图目录 + 原文件名) ### 视频编辑 1. 用户选视频 → `VideoEditViewModel.loadVideo()` 读取时长/分辨率 2. ExoPlayer 预览,支持时间裁剪(RangeSlider + 逐帧步进)和空间裁剪(可拖拽区域叠加层) 3. 去水印 → `VideoInpaintEffect` 实现 `GlEffect`,逐帧读回像素 → OpenCV `Photo.inpaint()` → 写回输出纹理 4. 导出 → Media3 `Transformer` 编码为 HEVC,保存至系统相册(原视频目录) ### 批量处理 - **批量去水印**:逐图调用 `WatermarkDetector.hasWatermark()`,有则处理无则跳过;结果卡片点击弹出 `ComparisonDialog` 大图对比 - **批量格式转换**:JPEG / PNG / WebP 互转 + 质量调节;结果卡片内联切换原图/处理后对比 - **批量裁剪**:按百分比裁剪图片边缘(顶部、底部、左边、右边),支持预览裁剪效果,结果卡片可切换原图/裁剪后对比 ### 排版打印 1. 用户选择排版方式(9 种预设布局:1×1~3×3,对应 1~9 张图片) 2. 从图库选择对应数量的图片 3. `LayoutPrintViewModel.combineBitmaps()` 在 `Dispatchers.Default` 将图片按比例居中适配到每个格子(1200×1200px),格子间 8px 间距 4. 生成预览图,用户确认后保存为 PNG(`BitmapUtils.saveBitmapToPublic()`) ### 批量保存反馈机制 批量操作(去水印/格式转换/裁剪)保存完成后,UI 通过 `saveMessage` 字段统一反馈: - **成功**:`✓ 已保存 N 张图片到 Pictures/xxx` - **失败**:`保存失败,共 N 张(具体原因)` — 通过 `BitmapUtils.lastSaveError` 透传底层错误 - **部分成功**:`成功 M 张,失败 N 张(具体原因)` 按钮仅在保存过程中禁用(`isSaving=true`),保存完成后始终可再次点击重试失败项。 ### Android 15 MediaStore 兼容性 `MediaStore.Images.Media` 仅允许保存到 `DCIM` 和 `Pictures` 两个目录。`BitmapUtils.saveBitmapToPublic()` 会自动检测并回退: - 原图在 `Download/`、`Documents/` 等非允许目录 → 自动保存到 `Pictures/` - 照片选择器 URI 查询 `RELATIVE_PATH` 失败 → 静默回退到 `Pictures/` - `Bitmap.CompressFormat.WEBP` 已弃用(API 30+)→ 自动转为 `WEBP_LOSSY` ## 关键交互说明 - **对比预览**:处理后画布右上角显示"查看原图"/"查看处理后"切换按钮 - **一键回退**:对比预览时点击"回退此步"回到处理前状态 - **多步撤销**:`EditViewModel.history` 栈保存处理前的 bitmap,`undo()` 可回退任意步 - **多次修改**:一次处理后可继续涂抹再处理,canvas 通过 `processVersion` key 自动重置 ## License Apache 2.0