Page MenuHomePhabricator

Lossless JPEG Blur & Edit Screen UI Revamp
Closed, DeclinedPublic

Description

GSoC 2026 Proposal — Lossless JPEG Blur & Edit Screen UI Revamp (Wikimedia Commons Android App)


Profile Information

FieldValue
NamePriyank Shankar
Web Profilegithub.com/shankarpriyank
LocationTokyo, Japan
Typical working hoursJST (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:

  1. 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.
  2. 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.
  3. 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.
  • Phabricator: T415446
  • GitHub Issue: #1192
  • Project Size: Large (350 hours / 12 weeks, ~29 hours/week)

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:

  1. Access the sparse coefficient array via reflection
  2. Preserve the DC coefficient (zigzag position 0) and optionally the lowest few AC coefficients
  3. Truncate or attenuate remaining high-frequency coefficients by a strength-dependent factor
  4. 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)
WeekFocusDeliverable
-2 to -1Write 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)
WeekFocusTasksDeliverable
1DCT reflection + blur algorithmImplement 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
2TransformImage integration + blur overlayAdd 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
3EditActivity integrationAdd "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
4Testing + stabilizationUnit 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)
WeekFocusTasksDeliverable
5ML Kit face detection + plate detection investigationAdd 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
6Detection 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
7Compose scaffold + tool modesChange 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
8Crop + blur overlays in Compose CanvasReimplement 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
9Compose integration + polishCoordinate 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
10Full integration testingFull 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)
WeekFocusTasksDeliverable
11Edge cases + code reviewProgressive 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
12Documentation + final submissionDeveloper 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

    1. 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)

Event Timeline

PriyankShankar renamed this task from Insert project title here to Lossless JPEG Blur & Edit Screen UI Revamp.Mar 21 2026, 5:11 PM

Thanks @PriyankShankar for a detailed proposal. Could you add specific details about your working hours, given that you've a full time job? Also, please add the answers to these questions to your proposal:

Hi @RitikaPahwa4444, thanks for questions! Here are the answers:

Eligibility for GSoC:
Yes, I am confident I meet the eligibility criteria. I am no longer a student, but I am no open source expert, so I believe I qualify as a participant.

Expectations from mentors:
I'd appreciate guidance on deciding the technical approach, code reviews on my PRs, and help with unblocking when I get stuck on a problem. My goal is to successfully complete this project, and having mentorship support in those areas would go a long way.

Other proposals:
No, I do not plan to submit any other proposals. This is the only project I'm focusing on.

Working hours:
I'm based in JST and have a full-time job with flexible hours. I want to be upfront that my day-to-day schedule can vary quite a bit — most days i am a night owl but some work days have a few meeting that i need to attend in the morning so i need to sleep early some days too. I don't want to commit to a fixed daily schedule that wouldn't reflect reality.

That said, to give a rough idea of when I'm generally available:

  • Weekdays: around 6:00 PM – 2-3 AM JST
  • Weekends: around 8:00 AM – 6:00 PM JST

But these are approximate windows, not guaranteed hours. What I can commit to is consistent weekly output and keeping the project on track regardless of how the hours are distributed across any given week.

Thanks for the review and suggestions. I have updated the proposal scope and timeline accordingly.

  1. Alignment with T415446 first bullet / #6659

I have now explicitly included the first bullet from T415446 (GitHub #6659) in the implementation plan and allocated dedicated time in the early phase so this work is not treated as optional. I will treat it as a required deliverable before/alongside the blur engine integration milestone.

  1. Eligibility

Yes, I confirm I am eligible for Google Summer of Code under the current program criteria.

  1. Expectations from mentors / mentorship program

My expectations are:

  • architecture and design feedback for major decisions (LLJTran reflection strategy, ML detection tradeoffs, Compose migration boundaries),
  • timely PR review with actionable feedback,
  • support in prioritization and risk management if scope adjustments are needed.
  1. Other proposals

No, this is the only proposal I plan to submit.

  1. Working hours and availability

I am in JST (UTC+9) with a full-time job and flexible schedule.
Typical availability windows:

  • Weekdays: ~18:00 to 02:00/03:00 JST
  • Weekends: ~08:00 to 18:00 JST

These are approximate windows; my main commitment is consistent weekly output, milestone-based progress updates, and reliable communication.

I will continue posting weekly progress updates and milestone status on T415446/T420828.

Hi, thanks for submitting your GSoC 2026 project proposal with Wikimedia!

Please make sure you’ve also submitted your proposal on the official Summer of Code website: https://summerofcode.withgoogle.com. The deadline for both submission and any edits is the same, so ensure everything is finalized before March 31, 18:00 UTC, as changes won’t be possible after that.

We strongly recommend completing any updates at least 30 minutes before the deadline to avoid last-minute glitches or unexpected technical issues.

Wishing you all the best for your application. Hope to see you as part of the program soon! 🚀

Hi, thank you for your submission and the effort you put into your proposal. This year we received over 380 strong applications, and unfortunately we were not able to offer you a slot. This was a very competitive process, and many high quality proposals could not be selected. We truly encourage you to stay engaged and continue contributing to Wikimedia projects. Over the years, many contributors who were not selected for Google Summer of Code have gone on to make impactful contributions and become long term members of the community. Please do not see this as a failure, but as a step forward in your journey. We would love to stay in touch and support your continued involvement.

If you would like guidance on how to contribute to our projects outside GSoC, feel free to reach out to any of the mentors or org admins, they will be happy to help you get started.

You can get started or continue contributing here:

We hope to see your contributions in our community soon.