EasyImagy makes it easy to process images in Swift.

var image = Image<RGBA<UInt8>>(named: "ImageName")!

print(image[x, y])
image[x, y] = RGBA(red: 255, green: 0, blue: 0, alpha: 127)
image[x, y] = RGBA(0xFF00007F) // red: 255, green: 0, blue: 0, alpha: 127

// Iterates over all pixels
for pixel in image {
    // ...
}

// Processes images (e.g. binarizations)
let binarized: Image<Bool> = image.map { $0.gray >= 127 }

// From/to `UIImage`
image = Image<RGBA<UInt8>>(uiImage: imageView.image!)
imageView.image = image.uiImageCopy the code

Introduction

Processing images by CoreGraphics is complicated: various formats, old C APIs and painful memory management. EasyImagy provides easier APIs to process images.

Typically the Image type is used with the RGBA type. The RGBA is a simple structure declared as follows.

struct RGBA<Channel> {
    var red: Channel
    var green: Channel
    var blue: Channel
    var alpha: Channel
}Copy the code

You can easily access to pixels using subscripts like image[x, y] and also their channels using properties red, green, blue and alpha.

In addition, Image and RGBA provide some powerful APIs to process images. For example, it is possible to convert an image to grayscale combining Image.map with RGBA.gray in one line as shown below.

let grayscale: Image<UInt8> = image.map { $0.gray }Copy the code

Another notable feature of EasyImagy is that the Image is a struct, i.e. a value type, with copy-on-write. It means

  • Image instances never be shared
  • defensive copying is unnecessary
  • no wastful copying of Image instances
  • copying is executed lazily when it is required
var another = image // Not copied here because of copy-on-write
another[x, y] = RGBA(0xff0000ff) // Copied here lazilyCopy the code

Usage

Import

import EasyImagyCopy the code

Initialization

let image = Image<RGBA<UInt8>>(named: "ImageName")!Copy the code
let image = Image<RGBA<UInt8>>(contentsOfFile: "path/to/file")!Copy the code
let image = Image<RGBA<UInt8>>(data: Data(/* ... * /))!Copy the code
let image = Image<RGBA<UInt8>>(uiImage: imageView.image!) // from a UIImageCopy the code
let image = Image<RGBA<UInt8>>(width: 640, height: 480, pixels: pixels) // from pixelsCopy the code
let image = Image<RGBA<UInt8>>(width: 640, height: 480, pixel: .black) // a black RGBA imageCopy the code
let image = Image<UInt8>(width: 640, height: 480, pixel: .min) // a black grayscale imageCopy the code
let image = Image<Bool>(width: 640, height: 480, pixel: false) // a black binary imageCopy the code

Access to a pixel

// Gets a pixel by subscripts
let pixel = image[x, y]Copy the code
// Sets a pixel by subscripts
image[x, y] = RGBA(0xFF0000FF)
image[x, y].alpha = 127Copy the code
// Safe get for a pixel
if let pixel = image.pixelAt(x: x, y: y) {
    print(pixel.red)
    print(pixel.green)
    print(pixel.blue)
    print(pixel.alpha)
    
    print(pixel.gray) // (red + green + blue) / 3
    print(pixel) // formatted like "#FF0000FF"
} else {
    // `pixel` is safe: `nil` is returned when out of bounds
    print("Out of bounds")
}Copy the code

Iteration

for pixel in image {
    ...
}Copy the code

Rotation

Let result = image.rotated(by:.pi) // All that is valuable is PICopy the code
let result = image.rotated(byDegrees: 180) // Rotated clockwise by 180 degreesCopy the code
General hospital of Rotated Rotated by PI / 4 and fill the background with red let result = image.pi / 4, extrapolatedBy: .filling(.red))Copy the code

Flip

let result = image.xReversed() // Flip HorizontallyCopy the code
let result = image.yReversed() // Flip VerticallyCopy the code

Resizing

let result = image.resizedTo(width: 320, height: 240)Copy the code
let result = image.resizedTo(width: 320, height: 240,
    interpolatedBy: .nearestNeighbor) // Nearest neighborCopy the code

Crop

Slicing is executed with no copying costs.

let slice: ImageSlice<RGBA<UInt8>> = image[32..<64, 32..<64] // No copying costs
let cropped = Image<RGBA<UInt8>>(slice) // Copying is executed hereCopy the code

Conversion

Image can be converted by map as well as Array. Followings are the examples.

Grayscale

let result: Image<UInt8> = image.map { (pixel: RGBA<UInt8>) -> UInt8 in
    pixel.gray
}Copy the code
// Shortened form
let result = image.map { $0.gray }Copy the code

Binarization

let result: Image<Bool> = image.map { (pixel: RGBA<UInt8>) -> Bool in
    pixel.gray >= 128
}Copy the code
// Shortened form
let result = image.map { $0.gray >= 128 }Copy the code

Binarization (auto threshold)

let threshold = UInt8(image.reduce(0) { $0 + $1.grayInt } / image.count)
let result = image.map { $0.gray >= threshold }Copy the code

Mean filter

Convoluted let kernel = Image<Float>(width: 3, height: 3, pixel: 1.0/9.0) let result = image.convoluted(kernel)Copy the code

Gaussian filter

let kernel = Image<Int>(width: 5, height: 5, pixels: [1, 4, 6, 4, 1, 4, 16, 24, 16, 4, 6, 24, 36, 24, 6, 4, 16, 24, 16, 4, 1, 4, 6, 4, 1, ).map {Float($0) / 256.0} let result = image.convoluted(kernel)Copy the code

With UIImage

// From `UIImage`
let image = Image<RGBA<UInt8>>(uiImage: imageView.image!)

// To `UIImage`
imageView.image = image.uiImageCopy the code

With NSImage

// From `NSImage`
let image = Image<RGBA<UInt8>>(nsImage: imageView.image!)

// To `NSImage`
imageView.image = image.nsImageCopy the code

Requirements

  • Swift 4 or later
  • Xcode 9 or later

Installation

Swift Package Manager

Package.swift

// Swift-tools-version :4.0 // The swift-tools-version declares The minimum version of swift required to build this package. import PackageDescription let package = Package( ... Dependencies: [. Package (url: "https://github.com/koher/EasyImagy.git", from: "0.4.0 - alpha"),], the targets: [ .target( ... dependencies: [ "EasyImagy", ]), ] )Copy the code

Carthage

Cartfile

Making "koher/EasyImagy" "0.4.0 - alpha."Copy the code

Manually

  1. Put EasyImagy.xcodeproj into your project/workspace in Xcode.
  2. Click your project icon and select the application target and the “General” tab.
  3. Add EasyImagy.framework to “Embedded Binaries”.

License

The MIT License