UIImage - implementing an auto levels algorithm
ML System Design practice on Codemia
Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.
Introduction
An auto-levels filter improves contrast by remapping pixel intensities so the useful part of the image spans more of the available range. On iOS, you can implement this for a UIImage by reading raw pixels, measuring channel ranges, and stretching the values back into 0...255.
What Auto Levels Actually Does
At a high level, auto levels finds the dark and bright points in an image and rescales the pixels between them. If an image is washed out, its histogram is often compressed into a narrow band, such as values between 60 and 180 instead of the full 0 to 255 range.
The simplest algorithm is:
- read every pixel
- find the minimum and maximum values for each channel
- remap every channel using linear scaling
The formula is:
scaled = (value - min) * 255 / (max - min)
That produces a stronger image, though real photo editors often use clipped percentiles instead of the absolute min and max so that one outlier pixel does not distort the whole result.
Convert UIImage To A Writable Pixel Buffer
To modify pixels directly, you need a CGImage, a bitmap context, and a byte buffer. The code below uses CoreGraphics and UIKit only, which makes it easy to drop into an iOS project.
At this point you have raw RGBA bytes in memory and can analyze them safely.
Measure Channel Ranges
For a simple auto-levels implementation, scan the red, green, and blue channels separately and record the minimum and maximum values.
You could also compute one range from luminance instead of per-channel ranges. Per-channel scaling often gives a more dramatic result, but it can also shift colors more aggressively.
Remap The Pixels
Once you know the ranges, stretch the channel values. Guard against min == max, because a flat channel cannot be normalized.
This is the core of the effect. If the original image only used a narrow intensity band, the output will have stronger contrast.
Make The Algorithm More Practical
Absolute min and max values are sensitive to outliers. A single dead-black or pure-white pixel can force the whole image to stretch around it. In production, a better version computes a histogram and clips a small percentage from both ends before scaling.
That approach works like this:
- build a histogram for each channel
- ignore the lowest and highest small percentile
- use the remaining bounds as the new black and white points
If you need high throughput, use Accelerate or Core Image for the histogram stage. If you need a readable first implementation, the manual byte-buffer approach above is easier to understand and debug.
Common Pitfalls
- Ignoring the alpha channel layout. If your bitmap format does not match the indexing logic, colors will be wrong.
- Stretching each channel independently without testing skin tones or neutral grays. Strong casts can appear.
- Using absolute min and max in noisy images. Outliers can make the correction weak.
- Repeatedly reprocessing the same image. Auto levels is best applied to the original source, not to an already modified result.
- Forgetting image orientation. Returning a
UIImagewithout preserving orientation metadata can produce rotated output.
Summary
- Auto levels improves contrast by remapping pixel values into a wider range.
- A simple implementation scans pixels, records per-channel min and max values, and rescales them.
- '
CGContextplus a byte buffer is enough for a clear Swift implementation.' - Production-quality filters usually clip histogram tails instead of using absolute extremes.
- Test the effect on real images, because aggressive per-channel stretching can distort color balance.
Related reading
- UIImage Resize, then Crop
- Unable to test and deploy a deeplabv3-mobilenetv2 tensorflow-lite segmentation model for inference
- Understanding COCO evaluation maximum detections
- Understanding Freeman chain codes for OCR
- Ukkonen's suffix tree algorithm in plain English
- Ukkonen's suffix tree algorithm in plain English
- UIImagePickerController error Snapshotting a view that has not been rendered results in an empty snapshot in iOS 7
- UIImageView - How to get the file name of the image assigned?

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
ML System Design practice on Codemia
Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.