GSoC 2026 Proposal — Lossless JPEG Blur & Edit Screen UI Revamp (Wikimedia Commons Android App)
Profile Information
| Field | Value |
|---|---|
| Name | Priyank Shankar |
| Web Profile | github.com/shankarpriyank |
| Location | Tokyo, Japan |
| Typical working hours | JST (UTC+9) |
Synopsis
Project Title
Lossless JPEG Blur and Edit Screen UI Revamp for the Wikimedia Commons Android App
Summary
Wikimedia Commons hosts freely usable media files. When contributors upload photographs, they often need to blur sensitive content — faces of bystanders, vehicle license plates — to comply with privacy expectations before images become globally accessible. Currently, any blur operation would require full JPEG re-encoding, which degrades image quality across the entire photograph, even regions that were not edited. This is unacceptable for an archival-quality media repository.
This project proposes lossless JPEG blur: JPEG images are encoded as 8×8 pixel blocks of DCT (Discrete Cosine Transform) coefficients. By selectively attenuating the high-frequency coefficients in only the blocks the user wants to blur — while leaving every other block byte-identical to the original — we achieve privacy-preserving edits with zero quality loss to untouched regions.
Building on my already-implemented lossless JPEG crop and rotation , this project will:
- Implement lossless JPEG blur via direct DCT coefficient manipulation using the existing LLJTran library. LLJTran already decodes and stores all DCT coefficients internally in a 5-dimensional sparse array (dct_coefs[mcu_row][mcu_col][block_within_mcu][2][num_nonzero_coefficients]). By accessing this private field via reflection and truncating the high-frequency entries for targeted blocks, we achieve true lossless blur with zero new native dependencies.
- Add automatic face and license plate detection using Google ML Kit (on-device), mapping detected bounding boxes to JPEG MCU block boundaries so regions can be blurred in one tap.
- Fully revamp the Edit screen UI using Jetpack Compose with Material3, establishing it as a reference architecture for the app's ongoing migration from XML views to Compose.
Possible Mentor(s)
Nicolas Raoul, Ritika Pahva
Have you contacted your mentors already?
They are already familiar with my work on the lossless crop and rotate features, and we talk in github
Deliverables
Technical Architecture Overview
Before the week-by-week breakdown, here is how the three deliverables are architecturally connected:
1. Lossless JPEG Blur Engine (LLJTran DCT reflection)
The app already depends on com.github.juanitobananas:AndroidMediaUtil (v1.0-1) which provides LLJTran for lossless crop and rotate. Crucially, LLJTran fully decodes and stores all DCT coefficients in memory after read(READ_ALL):
// Inside LLJTran (private field) private int[][][][][] dct_coefs; // dct_coefs[mcu_row][mcu_col][block_within_mcu][2][num_nonzero_coefficients] // [..][..][..][0][k] = coefficient value // [..][..][..][1][k] = zigzag position (0 = DC, 1–63 = AC)
The field is private with no public getter, but the library version is pinned and the internal structure is stable. Using Java reflection to access dct_coefs, we can manipulate individual blocks without any new native dependencies — the same library that already handles crop and rotate also powers blur.
The blur algorithm for each targeted 8×8 block:
- Access the sparse coefficient array via reflection
- Preserve the DC coefficient (zigzag position 0) and optionally the lowest few AC coefficients
- Truncate or attenuate remaining high-frequency coefficients by a strength-dependent factor
- Save with transform(NONE) — LLJTran re-encodes the modified coefficients, leaving untouched blocks byte-identical
Kotlin interface (extends existing TransformImage):
interface TransformImage { fun rotateImage(imageFile: File, degree: Int, savePath: File): File? fun cropImage(imageFile: File, left: Int, top: Int, width: Int, height: Int, savePath: File): File? fun blurRegions(imageFile: File, regions: List<BlurRegion>, strength: Float, savePath: File): File? } data class BlurRegion(val left: Int, val top: Int, val width: Int, val height: Int)
2. Auto-Detection (Google ML Kit, on-device)
- ML Kit Face Detection for faces (reliable, ~1.5 MB model)
- ML Kit Text Recognition v2 for license plates (filtered by aspect ratio + alphanumeric heuristics)
- Both run on a downsampled Bitmap; bounding boxes scale linearly to full resolution then map to MCU coordinates: mcuX = pixelX / mcuWidth
3. Compose UI Revamp
The Edit screen will be rewritten using ComponentActivity + setContent{} (following the SingleWebViewActivity pattern already in the codebase). State management via ViewModel + StateFlow + collectAsStateWithLifecycle(). The crop overlay (CropOverlayView, currently 398 lines of custom Canvas View) will be reimplemented as Compose Canvas with detectDragGestures. DI integration via CommonsApplicationComponent.inject().
sealed interface EditScreenState { data object Loading : EditScreenState data class Ready( val imagePath: String, val currentRotation: Int, val activeToolMode: ToolMode, val blurRegions: List<BlurRegion>, val detectedFaces: List<DetectedRegion>, val detectedPlates: List<DetectedRegion>, ) : EditScreenState data class Saving(val progress: Float) : EditScreenState data class Error(val message: String) : EditScreenState } enum class ToolMode { NONE, ROTATE, CROP, BLUR }
Week-by-Week Timeline
Why a large project (350 hours / 12 weeks,): The full scope — lossless blur engine, ML Kit auto-detection, and a complete Compose UI rewrite — warrants a large project. At ~29 hours/week, there is enough bandwidth for thorough testing, mentor-driven code review cycles, and a robust Compose migration that can serve as a reference architecture for the rest of the app. The hardest problems — MCU alignment, coordinate mapping, EXIF preservation, the LLJTran crop bug workaround — are already solved in the crop implementation, which de-risks the blur engine work and lets us invest more time in detection quality and UI polish.
Community Bonding Period (before coding starts)
| Week | Focus | Deliverable |
|---|---|---|
| -2 to -1 | Write a proof-of-concept that accesses dct_coefs via reflection, zeroes high-frequency entries in selected blocks, and produces a visually blurred output JPEG. Engage with mentors on blur UI design via Phabricator. Investigate feasibility of forking AndroidMediaUtil to expose a public dct_coefs getter as a fallback to reflection. | Working proof-of-concept; reflection access validated; fork feasibility assessed; UI wireframes reviewed by mentor |
Phase 1: Lossless JPEG Blur Engine + UI (Weeks 1–4)
| Week | Focus | Tasks | Deliverable |
|---|---|---|---|
| 1 | DCT reflection + blur algorithm | Implement LLJTranDctAccessor: reflection-based access to dct_coefs, mcuWidth, mcuHeight; add ProGuard keep rules. Implement blur: for each targeted MCU block, truncate the sparse coefficient array to keep only DC + N lowest-frequency AC entries (N controlled by strength 0.0–1.0); handle Y, Cb, Cr components respecting chroma subsampling. Unit tests verifying untouched blocks remain byte-identical after save(NONE); test across subsampling modes (4:2:0, 4:2:2, 4:4:4). | Working blur algorithm with verified byte-identity guarantee |
| 2 | TransformImage integration + blur overlay | Add blurRegions() to TransformImage interface and TransformImageImpl; full pipeline: read(READ_ALL) → reflect → modify → save(NONE) → EXIF preservation. Non-JPEG fallback: lossy blur via Bitmap operations with user notice. Implement blur overlay View (following CropOverlayView pattern): touch-and-drag rectangles, resizable handles, semi-transparent overlay, multiple region support. | blurRegions() producing lossless output + interactive overlay UI |
| 3 | EditActivity integration | Add "Blur" button to bottom bar; wire blur regions → EditViewModel.blurImage() → TransformImageImpl; pixel-to-MCU coordinate mapping (reusing the percentage-based approach from convertViewCropToImageCrop); handle operation ordering: rotate → crop → blur. | Full blur pipeline: draw region → save → lossless output |
| 4 | Testing + stabilization | Unit tests: image edges, tiny images, grayscale JPEGs; integration tests for EXIF preservation through rotate → crop → blur; edge cases (corrupted JPEGs, large images >50MP, progressive JPEGs); bug fixes from mentor review. | Midterm deliverable: Working lossless JPEG blur, demo-ready |
⭐ Midterm Evaluation
Deliverable: Functional lossless JPEG blur integrated into the edit screen. Users can draw rectangular regions and blur them without quality loss to the rest of the image. All blur tests passing. Non-JPEG fallback working. Byte-identity of untouched regions verified.
Phase 2: Auto-Detection + Compose UI Migration (Weeks 5–10)
| Week | Focus | Tasks | Deliverable |
|---|---|---|---|
| 5 | ML Kit face detection + plate detection investigation | Add com.google.mlkit:face-detection dependency; create FaceDetectionManager; run on downsampled Bitmap; map bounding boxes from downsampled → full-res → MCU coordinates; present as pre-selected regions on blur overlay. Face detection is well-supported by ML Kit and will be the primary auto-detect feature. For license plate detection, further investigation is needed during this week to determine the best approach — ML Kit Text Recognition v2 with heuristic filtering (aspect ratio + alphanumeric content) is one candidate, but alternatives (ML Kit Object Detection, a lightweight custom model, or a simpler user-driven approach) will be evaluated based on accuracy and false positive rates. The final approach will be decided in consultation with mentors. | One-tap face auto-blur working; plate detection approach investigated and decided |
| 6 | Detection UX + ViewModel refactor | "Auto Blur" button with toggleable selections, confidence thresholds, dismiss/add controls, padding around detected regions. Refactor EditViewModel: MutableStateFlow/asStateFlow(), EditScreenState sealed interface, EditViewModelFactory with Dagger injection (following CategoryDetailsViewModel.ViewModelFactory pattern), inject TransformImage. | Polished auto-detection UX; reactive ViewModel with DI |
| 7 | Compose scaffold + tool modes | Change EditActivity to ComponentActivity + setContent{}; Scaffold + top bar + image display + bottom tool bar. Tool mode state machine (None/Rotate/Crop/Blur). | Edit screen rendering in Compose |
| 8 | Crop + blur overlays in Compose Canvas | Reimplement CropOverlayView as Compose Canvas composable; detectDragGestures for 9-handle interaction; rule-of-thirds grid. Blur region overlay: auto-detected regions with toggleable selection, manual drawing, blurred visual preview via BlurEffect (preview only; save uses lossless DCT); "Apply All Detected" button. | Both overlays functional in Compose |
| 9 | Compose integration + polish | Coordinate mapping between Compose image display and actual pixels; rotation animation with animateFloatAsState; verify all transformations work end-to-end. Material3 dark/light theme; content descriptions; TalkBack compatibility; blur strength slider; edge-to-edge insets; undo/redo for blur regions; progress indicator during save. | Accessible, polished Compose UI with all tool modes |
| 10 | Full integration testing | Full workflow test: open → detect → auto-blur → manual blur → crop → rotate → save; Compose UI tests (ComposeTestRule); verify editedImageFilePath contract with UploadMediaDetailFragment; ViewModel survives configuration changes; performance profiling on real devices. | All features working end-to-end, test suite passing |
Phase 3: Hardening + Final Delivery (Weeks 11–12)
| Week | Focus | Tasks | Deliverable |
|---|---|---|---|
| 11 | Edge cases + code review | Progressive JPEGs; grayscale JPEGs; EXIF orientation edge cases; MCU boundary alignment tests for all subsampling modes; large images (>50MP); detection false positive tuning; address accumulated mentor review feedback; ensure code style consistency. | Robust edge case handling; all review feedback addressed |
| 12 | Documentation + final submission | Developer docs for blur architecture and Compose migration; final bug fixes; CI green on all PRs; write GSoC final report summarizing work, challenges, and outcomes; ensure all PRs are merged or ready for merge. | Final deliverable submitted |
Participation
- Communication: Weekly progress updates on Phabricator (T415446) and the constant communication over email and github, Will post brief updates after each milestone and longer write-ups at midterm and final evaluation.
- Source code: All code will be developed on feature branches in the commons-app/apps-android-commons GitHub repository. PRs will be submitted for review at logical milestones (end of each phase, at minimum).
- Blog: Will write 1-2 blog posts during the program documenting the lossless blur technique and Compose migration approach, to benefit the broader open-source community.
About Me
Education
Bachelor's degree in Computer Science, completed in 2024.
How did you hear about this program?
I first heard about Google Summer of Code while I was in college.
Other time commitments
I have a full-time job during the coding period. The 350-hour large project is manageable alongside my work schedule.
Applying to both GSoC and Outreachy?
No.
What does making this project happen mean to you?
This project is deeply personal to me. I first developed the lossless rotate feature a long time ago while I was still a student, and recently I came back to implement the crop feature as well. This GSoC period would be like wrapping it all up — adding the blur feature and then overhauling the edit screen UI/UX so the entire experience is much better for users. It gives me real satisfaction knowing that my contributions will be used by people around the world to support the Wikimedia Commons initiative. Beyond that, there is a deep technical satisfaction in completing what has been a multi-year journey through JPEG internals, lossless image processing, and now modern Android architecture — seeing this project through from start to finish.
Past Experience
- Contributions to Wikimedia Commons Android App
- Lossless JPEG Crop and Rotate — Implemented the crop and rotate features, apart from other contributions:
- #6645 — Feat: Add crop functionality to image editing with overlay support (merged Mar 2026)
- #5252 — Lossless Transformations (Rotate feature) (merged Oct 2023)
- #5430 — Fixed bitmap too large issue (merged Jan 2024)
- #5188 — Enable support toolbar for ProfileActivity and added unit tests (merged Apr 2023)
- #5185 — Fixed misleading warning title for duplicate file name (merged Apr 2023)
- Other Open Source Experience
- GSoC 2022 — Developing Support For Android Auto (Organic Maps, mentored by Roman Tsisyk): Added Android Auto and Android OS support to the Organic Maps navigation app, implementing shared code paths so the app works on both Android Automotive OS and Android Auto. (Blog write-up)