# How to Recognize QR Codes from an Image Using EFQRCode: A Complete Guide

> Easily recognize QR codes from images with EFQRCode. This guide shows how to use the built-in recognizer with Core Image for automatic grayscale processing and reliable QR code extraction.

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

---

**EFQRCode provides a built-in recognizer class that uses Core Image's `CIDetector` to extract QR code payloads from any `CGImage`, with automatic fallback to grayscale processing for difficult-to-read codes.**

EFQRCode is a lightweight, pure-Swift library for generating and recognizing QR codes across all Apple platforms. When you need to recognize QR codes from an image using EFQRCode, the library leverages Core Image's built-in detector with a two-pass accuracy strategy to maximize recognition rates on iOS, macOS, tvOS, watchOS, and visionOS.

## How EFQRCode Recognition Works

The recognition engine is implemented in **`Source/EFQRCode+Recognizer.swift`**. It wraps Core Image's `CIDetector` in a platform-agnostic API that accepts any `CGImage` and returns an array of decoded strings.

The recognizer is designed to be self-contained and cache-friendly. It stores successful results in a private `contentArray` property, and automatically clears this cache whenever the input image changes via a `didSet` observer.

## Step-by-Step Recognition Pipeline

### Input: CGImage

The public API expects a `CGImage` rather than a UI-specific type like `UIImage` or `NSImage`. This design keeps the recognizer portable across all Swift-supported Apple platforms. In **`Source/Extension/CGImage+EFQRCode.swift`**, the library provides convenience methods to convert between `CGImage`, `CIImage`, and grayscale representations.

### Core Image Conversion and Detection

Once initialized with a `CGImage`, the recognizer calls `ciImage()` (defined in **`CGImage+EFQRCode.swift`**, lines 27-30) to wrap the Core Graphics image in a `CIImage`.

Detection happens in **`Source/Extension/CIImage+EFQRCode.swift`** via the `recognizeQRCode(options:)` method. This creates a `CIDetector` of type `CIDetectorTypeQRCode` with the `CIDetectorAccuracyHigh` option, then extracts all `CIQRCodeFeature` instances and maps their `messageString` properties to the result array.

### Grayscale Fallback Strategy

If the high-accuracy scan returns an empty array, the recognizer implements a robust fallback mechanism. It calls `CGImage.grayscale()` (lines 10-24 of **`CGImage+EFQRCode.swift`**) to create a low-contrast grayscale bitmap context, then repeats detection with `CIDetectorAccuracyLow`. This two-pass approach significantly improves recognition rates for images with poor lighting, color interference, or low contrast.

### Result Caching

The recognizer maintains an internal `contentArray` to cache decoded strings. When you call `recognize()`, the method first checks this cache; if nil, it executes the full detection pipeline. Changing the `image` property automatically invalidates the cache via a `didSet` observer, ensuring you never receive stale results when processing new images.

## Practical Code Examples

### Basic QR Code Recognition

This minimal example demonstrates loading an image and extracting QR codes:

```swift
import EFQRCode
import CoreGraphics

// Load a CGImage (UIImage/NSImage → .cgImage works on all platforms)
guard let cgImage = UIImage(named: "qrcodeSample")?.cgImage else {
    fatalError("Unable to load test image")
}

// Initialize the recognizer
let recognizer = EFQRCode.Recognizer(image: cgImage)

// Perform recognition
let codes = recognizer.recognize()

if codes.isEmpty {
    print("No QR codes found")
} else {
    for (index, code) in codes.enumerated() {
        print("QR code #\(index): \(code)")
    }
}

```

The recognizer automatically falls back to grayscale processing if the initial scan fails.

### Generate and Recognize Round-Trip

This example mirrors the unit test in **[`Tests/Tests.swift`](https://github.com/efprefix/efqrcode/blob/main/Tests/Tests.swift)** (lines 51-62), demonstrating a complete workflow:

```swift
import EFQRCode

let payload = "https://github.com/EFPrefix/EFQRCode"

// Generate a QR code
let generator = try EFQRCode.Generator(payload, style: .basic(params: .init()))
let cgImage = try generator.toImage(width: 256).cgImage()

// Recognize the generated image
let detected = EFQRCode.Recognizer(image: cgImage).recognize()
assert(detected.first == payload, "Recognition failed")
print("Successfully recognized: \(detected[0])")

```

### Handling Multiple QR Codes

The recognizer always returns an array because a single image may contain multiple codes:

```swift
let multiCodeImage: CGImage = // obtain image containing several QR codes
let results = EFQRCode.Recognizer(image: multiCodeImage).recognize()
print("Found \(results.count) QR codes:")
results.forEach { print($0) }

```

## Key Source Files and Architecture

| File | Description |
|------|-------------|
| **`Source/EFQRCode+Recognizer.swift`** | Public `EFQRCode.Recognizer` class that orchestrates detection, caching, and fallback logic. |
| **`Source/Extension/CGImage+EFQRCode.swift`** | `CGImage` utilities including PNG export, grayscale conversion, and the `ciImage()` bridge to Core Image. |
| **`Source/Extension/CIImage+EFQRCode.swift`** | Core Image helpers including conversion to `CGImage` and the `recognizeQRCode(options:)` detector implementation. |
| **[`Tests/Tests.swift`](https://github.com/efprefix/efqrcode/blob/main/Tests/Tests.swift)** | Unit tests demonstrating generation and recognition round-trips; useful reference for implementation patterns. |
| **[`README.md`](https://github.com/efprefix/efqrcode/blob/main/README.md)** | High-level documentation covering recognition workflows and platform requirements. |

All Core Image extensions are guarded with `#if canImport(CoreImage)` to ensure compilation only on supported platforms.

## Summary

- **EFQRCode.Recognizer** provides a platform-agnostic API for QR code recognition using `CGImage` input.
- The recognition pipeline uses **Core Image's `CIDetector`** with a two-pass strategy: high accuracy first, then grayscale fallback with low accuracy.
- **Automatic caching** prevents redundant processing when calling `recognize()` multiple times on the same image.
- The implementation spans **`EFQRCode+Recognizer.swift`**, **`CGImage+EFQRCode.swift`**, and **`CIImage+EFQRCode.swift`**.
- All Apple platforms supporting Core Image (iOS, macOS, tvOS, watchOS, visionOS) are supported.

## Frequently Asked Questions

### What image format does EFQRCode require for recognition?

EFQRCode's recognizer accepts any `CGImage` regardless of original format (PNG, JPEG, HEIC, etc.). The library handles conversion to `CIImage` internally via the `ciImage()` extension in **`CGImage+EFQRCode.swift`**. If you're working with `UIImage` (iOS) or `NSImage` (macOS), simply access their `.cgImage` property before passing to the recognizer.

### Does EFQRCode support recognizing multiple QR codes in one image?

Yes. The `recognize()` method always returns an array of strings (`[String]`) because a single image may contain multiple QR codes. The underlying `CIDetector` in **`CIImage+EFQRCode.swift`** extracts all `CIQRCodeFeature` objects from the image, and the recognizer maps each feature's `messageString` to the result array.

### What platforms support EFQRCode recognition?

Recognition works on any platform that supports **Core Image**: iOS, macOS, tvOS, watchOS, and visionOS. The relevant code is wrapped in `#if canImport(CoreImage)` compiler directives to ensure the library compiles only when Core Image is available. Linux and other non-Apple platforms are not supported for recognition because they lack the `CIDetector` API.

### How does EFQRCode handle difficult-to-read QR codes?

EFQRCode implements a robust two-pass detection strategy in **`EFQRCode+Recognizer.swift`**. First, it attempts recognition with `CIDetectorAccuracyHigh` on the original image. If that returns no results, it automatically converts the image to grayscale using `CGImage.grayscale()` (defined in **`CGImage+EFQRCode.swift`**) and retries with `CIDetectorAccuracyLow`. This fallback mechanism significantly improves recognition rates for low-contrast, shadowed, or color-distorted images.