Skip to main content

Concurrency & Performance

Image Filter

Difficulty: Hard

In this challenge, you'll build an app that applies a blur filter to a small image by replacing each pixel with the average RGB value of its surrounding pixels. The app will display both the original and blurred images side by side. The challenge is divided into multiple stages, progressively focusing on image processing, performance optimization, and concurrency.

Overview

This is a single-screen application with the following components:

  • Image Import – Load a sample image and convert it into a 2D pixel array (each pixel is represented as image[row][column]).
  • Blur Implementation – Write an algorithm that modifies each pixel based on its neighboring pixels.
  • Concurrency – Optimize the blur process using concurrency techniques for improved speed.
  • Dynamic Blur (Optional) – Allow the user to dynamically adjust blur intensity and see real-time updates.

Stage 1: Image Import

Load a small sample image (provided via this guide) and convert it into a 2D array of pixel values for processing.

  • Load the sample image bundled with the app.
  • Extract pixel RGB values into a structured 2D array, where each pixel is accessible at image[row][column].
    • iOS: We can use CGImage or CIImage to extract pixel data.
    • Android: We can use Bitmap to read and manipulate pixel values.
  • Ensure the original image is rendered before applying the blur effect.

Stage 2: Blur Implementation

Write a blur algorithm that modifies pixel values based on their neighbors.

  • Process Individual Pixels:
    • Replace each pixel’s RGB values with the average of its neighboring pixels.
    • Neighboring pixels include the surrounding pixels in all eight possible directions (top, bottom, left, right, and diagonals).
    • The average RGB value should be calculated separately for each channel. Compute the sum of red values, then divide by the total number of contributing pixels. Repeat the same for green and blue values.
    • Ensure boundary pixels are handled correctly (e.g., edge pixels have fewer neighbors).
    • Prevent out-of-bounds memory access when averaging surrounding pixels.
  • Construct the blurred image: Convert the processed pixel array back into an image and display it.
    • iOS: We can use CGContext or CIImage to reconstruct the image.
    • Android: We can use Bitmap and Canvas to create an updated image.

Stage 3: Concurrency

Enhance the performance of the blur operation by processing different sections of the image in parallel.

  • Divide the image into sections as you see fit (rows, columns, or quadrants) and process them simultaneously.
  • Ensure that final pixel calculations are merged safely without race conditions.
  • Keep the UI responsive by ensuring the image processing happens in the background.
  • Prevent race conditions or inconsistencies when combining processed image sections.

Stage 4 (Optional): Dynamic Blur

Allow the user to adjust the blur radius dynamically and reapply the filter in real time.

  • Add a slider that lets the user adjust the blur radius between 1–5 pixels. Update the blurred image in real time as the radius changes.
  • Understanding Blur Radius: The blur radius defines how many surrounding pixels influence each pixel’s new color. A radius of 1 means only immediate neighbors (8 pixels) are considered. A radius of 5 means the 25x25 surrounding pixels contribute to the blur effect.