SwiftImage makes it easy to access pixels of images. The Image type in SwiftImage can be used intuitively like 2D Array.
var image: Image<UInt8> = Image(width: 640, height: 480, pixels: [255, 248, /* ... */])
let pixel: UInt8 = image[x, y]
image[x, y] = 255
let width: Int = image.width // 640
let height: Int = image.height // 480
We can also access pixels of images using CoreGraphics. However, CoreGraphics requires us to struggle with complicated formats, old C APIs and painful memory management. SwiftImage provides clear and Swifty APIs for images.
Typically Image is used with the RGBA type. RGBA is a simple struct declared as follows.
struct RGBA<Channel> {
var red: Channel
var green: Channel
var blue: Channel
var alpha: Channel
}
Because RGBA is a generic type, it can represent various formats of pixels. For example, RGBA<UInt8> represents a pixel of 8-bit RGBA image (each channel has a value in 0...255). Similarly, RGBA<UInt16> represents a pixel of 16-bit RGBA image (0...65535). RGBA<Float> can represent a pixel whose channels are Floats, which is often used for machine learning. A pixel of binary images, which have only black or white pixels and are used for fax, can be represented using RGBA<Bool>.
When RGBA is used with Image, type parameters are nested like Image<RGBA<UInt8>> because both Image and RGBA are generic types. On the other hand, grayscale images can be represented without nested parameters: Image<UInt8> for 8-bit grayscale images and Image<UInt16> for 16-bit grayscale images.
Image and RGBA provide powerful APIs to handle images. For example, it is possible to convert a RGBA image to grayscale combining Image.map with RGBA.gray in one line.
let image: Image<RGBA<UInt8>> = // ...
let grayscale: Image<UInt8> = image.map { $0.gray }
Another notable feature of SwiftImage is that Image is a struct with value semantics, which is achieved using copy-on-write. Therefore,
Image instances never be shared
defensive copying is unnecessary
there are no wasteful copying of Image instances
copying is executed lazily only when it is inevitable
var another: Image<UInt8> = image // Not copied here because of copy-on-write
another[x, y] = 255 // Copied here lazily
another[x, y] == image[x, y] // false: Instances are never shared
Usage
Import
import SwiftImage
Initialization
let image = Image<RGBA<UInt8>>(named: "ImageName")!
let image = Image<RGBA<UInt8>>(contentsOfFile: "path/to/file")!
let image = Image<RGBA<UInt8>>(data: Data(/* ... */))!
let image = Image<RGBA<UInt8>>(uiImage: imageView.image!) // from a UIImage
let image = Image<RGBA<UInt8>>(nsImage: imageView.image!) // from a NSImage
let image = Image<RGBA<UInt8>>(cgImage: cgImage) // from a CGImage
let image = Image<RGBA<UInt8>>(width: 640, height: 480, pixels: pixels) // from a pixel array
let image = Image<RGBA<UInt8>>(width: 640, height: 480, pixel: .black) // a black RGBA image
let image = Image<UInt8>(width: 640, height: 480, pixel: 0) // a black grayscale image
let image = Image<Bool>(width: 640, height: 480, pixel: false) // a black binary image
Access to a pixel
// Gets a pixel by subscripts
let pixel = image[x, y]
// Sets a pixel by subscripts
image[x, y] = RGBA(0xFF0000FF)
image[x, y].alpha = 127
// 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")
}
Iteration
for pixel in image {
...
}
Rotation
let result = image.rotated(by: .pi) // Rotated clockwise by π
let result = image.rotated(byDegrees: 180) // Rotated clockwise by 180 degrees
// Rotated clockwise by π / 4 and fill the background with red
let result = image.rotated(by: .pi / 4, extrapolatedBy: .filling(.red))
Flip
let result = image.xReversed() // Flip Horizontally
let result = image.yReversed() // Flip Vertically
Resizing
let result = image.resizedTo(width: 320, height: 240)
let result = image.resizedTo(width: 320, height: 240,
interpolatedBy: .nearestNeighbor) // Nearest neighbor
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 here
Conversion
Image can be converted by map in the same way as Array. Followings are the examples.
Grayscale
let result: Image<UInt8> = image.map { (pixel: RGBA<UInt8>) -> UInt8 in
pixel.gray
}
// Shortened form
let result = image.map { $0.gray }
Binarization
let result: Image<Bool> = image.map { (pixel: RGBA<UInt8>) -> Bool in
pixel.gray >= 128
}
// Shortened form
let result = image.map { $0.gray >= 128 }
Binarization (auto threshold)
let threshold = UInt8(image.reduce(0) { $0 + $1.grayInt } / image.count)
let result = image.map { $0.gray >= threshold }
Mean filter
let kernel = Image<Float>(width: 3, height: 3, pixel: 1.0 / 9.0)
let result = image.convoluted(kernel)
SwiftImage
SwiftImage is an image library written in Swift, which provides Swifty APIs and image types with value semantics.
Introduction
SwiftImage makes it easy to access pixels of images. The
Image
type in SwiftImage can be used intuitively like 2DArray
.We can also access pixels of images using CoreGraphics. However, CoreGraphics requires us to struggle with complicated formats, old C APIs and painful memory management. SwiftImage provides clear and Swifty APIs for images.
Typically
Image
is used with theRGBA
type.RGBA
is a simplestruct
declared as follows.Because
RGBA
is a generic type, it can represent various formats of pixels. For example,RGBA<UInt8>
represents a pixel of 8-bit RGBA image (each channel has a value in0...255
). Similarly,RGBA<UInt16>
represents a pixel of 16-bit RGBA image (0...65535
).RGBA<Float>
can represent a pixel whose channels areFloat
s, which is often used for machine learning. A pixel of binary images, which have only black or white pixels and are used for fax, can be represented usingRGBA<Bool>
.When
RGBA
is used withImage
, type parameters are nested likeImage<RGBA<UInt8>>
because bothImage
andRGBA
are generic types. On the other hand, grayscale images can be represented without nested parameters:Image<UInt8>
for 8-bit grayscale images andImage<UInt16>
for 16-bit grayscale images.Image
andRGBA
provide powerful APIs to handle images. For example, it is possible to convert a RGBA image to grayscale combiningImage.map
withRGBA.gray
in one line.Another notable feature of SwiftImage is that
Image
is astruct
with value semantics, which is achieved using copy-on-write. Therefore,Image
instances never be sharedImage
instancesUsage
Import
Initialization
Access to a pixel
Iteration
Rotation
Flip
Resizing
Crop
Slicing is executed with no copying costs.
Conversion
Image
can be converted bymap
in the same way asArray
. Followings are the examples.Grayscale
Binarization
Binarization (auto threshold)
Mean filter
Gaussian filter
With
UIImage
With
NSImage
With CoreGraphics
Requirements
Installation
Swift Package Manager
Carthage
Manually
SwiftImage.framework
to “Embedded Binaries”.License
The MIT License