# How EFQRCode QR Code Recognition Accuracy Works: Core Image's Two-Stage Detection Strategy

> Discover how EFQRCode enhances QR code recognition accuracy with its innovative two-stage detection strategy. Learn how it processes images for reliable results.

- Repository: [EFPrefix/efqrcode](https://github.com/efprefix/efqrcode)
- Tags: internals
- Published: 2026-03-01

---

**EFQRCode achieves robust QR code recognition accuracy by implementing a two-stage detection pipeline that first attempts high-accuracy scanning on the original image, then falls back to low-accuracy detection on a grayscale conversion if no codes are found.**

EFQRCode is a lightweight, pure Swift library for generating and recognizing QR codes. Understanding how EFQRCode QR code recognition accuracy works requires examining its integration with Core Image's `CIDetector` API and the intelligent fallback mechanism designed to handle both clean and challenging image conditions.

## The Two-Stage Detection Pipeline

The recognition system in `EFQRCode+Recognizer.swift` employs a cascading strategy that balances detection precision with processing speed. This approach ensures maximum accuracy for high-quality images while maintaining the ability to decode degraded or low-contrast inputs.

### Stage 1: High-Accuracy Detection on Original Image

The recognizer first creates a `CIDetector` instance configured with `CIDetectorTypeQRCode` and the `CIDetectorAccuracyHigh` option. This setting instructs Core Image to perform sophisticated image analysis including edge detection and contrast enhancement to locate QR code patterns.

According to the source in `EFQRCode+Recognizer.swift` (lines 15-23), the high-accuracy pass runs immediately on the original `CGImage`. If this stage successfully detects QR code features, the `messageString` values are extracted from the resulting `CIQRCodeFeature` objects and returned.

### Stage 2: Grayscale Fallback with Low-Accuracy Detection

If the high-accuracy pass returns no results, the pipeline triggers a fallback mechanism. The image is first converted to grayscale using the `grayscale()` extension method defined in `CGImage+EFQRCode.swift` (lines 10-18). This conversion enhances contrast for difficult images captured in low lighting or with noisy backgrounds.

The recognizer then invokes the `CIDetector` again, this time with `CIDetectorAccuracyLow`. While low-accuracy detection is faster and less computationally intensive, it is more sensitive to the improved contrast provided by the grayscale conversion. This combination allows the system to decode QR codes that the high-accuracy detector missed due to color interference or subtle contrast variations.

### Caching for Performance

The `Recognizer` class implements an internal caching mechanism via the `contentArray` property. When recognition is performed, results are stored in this cache to prevent redundant detection work when the same image is queried repeatedly.

The cache automatically invalidates when the `image` property changes, implemented through a `didSet` observer in `EFQRCode+Recognizer.swift`. This ensures that stale results are never returned when processing new input images.

## Core Implementation Details

The recognition accuracy pipeline spans three primary source files:

- **`Source/EFQRCode+Recognizer.swift`**: Contains the `Recognizer` class that orchestrates the two-stage detection logic, manages caching, and handles the grayscale fallback sequence.

- **`Source/Extension/CIImage+EFQRCode.swift`**: Provides the `recognizeQRCode(options:)` helper method that instantiates the `CIDetector` with configurable accuracy settings and extracts `messageString` values from detected features.

- **`Source/Extension/CGImage+EFQRCode.swift`**: Implements the `grayscale()` conversion function used during the fallback stage to enhance image contrast before low-accuracy detection.

## Practical Code Examples

### Basic Usage with Automatic Fallback

The simplest approach uses the `Recognizer` class, which automatically handles both high-accuracy detection and the grayscale fallback:

```swift
import EFQRCode
import CoreGraphics

let cgImage: CGImage = // ... your image source

let recognizer = EFQRCode.Recognizer(image: cgImage)
let contents = recognizer.recognize()

if let firstCode = contents.first {
    print("Detected QR code: \(firstCode)")
} else {
    print("No QR code found")
}

```

This implementation internally executes the two-stage pipeline defined in `EFQRCode+Recognizer.swift`, attempting `CIDetectorAccuracyHigh` first, then converting to grayscale and using `CIDetectorAccuracyLow` if necessary.

### Manual Control of Detection Accuracy

For scenarios requiring explicit control over the detection process, use the `CIImage` extension directly:

```swift
import CoreImage
import EFQRCode

let ciImage = CIImage(cgImage: cgImage)

// High-accuracy detection for maximum precision
let highAccuracyResults = ciImage.recognizeQRCode(
    options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]
)

// Low-accuracy detection for faster processing
let lowAccuracyResults = ciImage.recognizeQRCode(
    options: [CIDetectorAccuracy: CIDetectorAccuracyLow]
)

```

The `recognizeQRCode(options:)` method in `CIImage+EFQRCode.swift` forwards the supplied options dictionary directly to the `CIDetector` initializer, allowing full configuration of Core Image's detection parameters.

### Implementing Custom Grayscale Fallback

To manually replicate the library's fallback behavior or integrate it into custom processing pipelines:

```swift
import EFQRCode

let originalImage: CGImage = // ... input image

// Attempt high-accuracy recognition first
let initialResults = originalImage.ciImage().recognizeQRCode(
    options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]
)

if initialResults.isEmpty {
    // Manually convert to grayscale and try low accuracy
    if let grayscaleImage = try? originalImage.grayscale() {
        let fallbackResults = grayscaleImage.ciImage().recognizeQRCode(
            options: [CIDetectorAccuracy: CIDetectorAccuracyLow]
        )
        print("Fallback detection results: \(fallbackResults)")
    }
}

```

The `grayscale()` method in `CGImage+EFQRCode.swift` creates a new bitmap context with grayscale color space, improving contrast for the subsequent low-accuracy detection pass.

## Summary

- **EFQRCode implements a two-stage recognition pipeline** that first attempts `CIDetectorAccuracyHigh` on the original image, then falls back to `CIDetectorAccuracyLow` on a grayscale conversion if no codes are detected.

- **The high-accuracy stage** uses Core Image's sophisticated edge detection and contrast analysis to locate QR codes in clean, high-quality images.

- **The grayscale fallback** enhances contrast for challenging images (low light, noise) before applying the faster, less tolerant low-accuracy detector.

- **Internal caching** via `contentArray` prevents redundant processing when repeatedly querying the same image, with automatic cache invalidation when the input image changes.

- **Key source files** include `EFQRCode+Recognizer.swift` for orchestration, `CIImage+EFQRCode.swift` for detection configuration, and `CGImage+EFQRCode.swift` for image preprocessing.

## Frequently Asked Questions

### What is the difference between CIDetectorAccuracyHigh and CIDetectorAccuracyLow in EFQRCode?

`CIDetectorAccuracyHigh` instructs Core Image to perform computationally intensive analysis including edge detection and contrast enhancement, maximizing detection rates for clean images at the cost of processing speed. `CIDetectorAccuracyLow` uses faster, less sophisticated algorithms that consume fewer resources but are more sensitive to image quality and contrast variations. EFQRCode uses high accuracy first for precision, then falls back to low accuracy on grayscale images to balance speed and robustness.

### Why does EFQRCode convert images to grayscale during recognition?

Grayscale conversion occurs during the fallback stage when the initial high-accuracy detection fails to locate any QR codes. The `grayscale()` method in `CGImage+EFQRCode.swift` creates a bitmap context using a grayscale color space, which eliminates color noise and enhances contrast between the QR code's dark modules and light background. This preprocessing allows the low-accuracy detector to successfully decode images captured in low-light conditions or containing color interference that confused the initial high-accuracy pass.

### How does EFQRCode handle multiple QR codes in a single image?

The `Recognizer` class processes all detectable QR codes within the image bounds. The `recognize()` method returns an array of strings (`[String]`) containing the `messageString` values extracted from each `CIQRCodeFeature` detected by Core Image. The internal `contentArray` cache stores these results, ensuring that subsequent calls to `recognize()` on the same image return the cached array immediately without re-running the detection pipeline. Developers access individual codes by indexing into the returned array, with the order determined by Core Image's detection sequence.

### Can developers customize the recognition accuracy settings in EFQRCode?

While the high-level `Recognizer` class automatically manages the two-stage accuracy pipeline, developers can access lower-level APIs for manual control. The `CIImage` extension provides `recognizeQRCode(options:)`, which accepts a dictionary including `CIDetectorAccuracy` with values `CIDetectorAccuracyHigh` or `CIDetectorAccuracyLow`. This allows developers to bypass the automatic fallback logic for scenarios requiring specific performance characteristics, such as real-time scanning where low-accuracy speed is preferred over maximum detection rates, or archival processing where high-accuracy precision is mandatory regardless of processing time.