t-ae/swim
Cross platform image library for Swift.
API
### Generic `Image` type
```swift
struct Image<P: PixelType, T: DataType>
```
#### Supported types
`PixelType`: `Gray`, `GrayAlpha`, `RGB`, `RGBA`, `ARGB`
`DataType`: `Bool`, `UInt8`, `Int`, `Float`, `Double`, `Complex<T: BinaryFloatingPoint>`
Some functions assume pixel values are:
- in [0, 255] range if `DataType` is integer.
- in [0, 1] range if `DataType` is floating point.
### Creation
```swift
let image = Image<RGBA, UInt8>(width: 3, height: 5, data: uint8Array)
// Can use type inference
let gray = Image(width: 3, height: 20, gray: intArray)
let rgb = Image(width: 4, height: 5, rgb: floatArray)
let rgba = Image(width: 3, height: 5, rgba: doubleArray)
let argb = Image(width: 5, height: 3, argb: uint8Array)
// Filled with values/colors
let zero = Image<RGBA, Double>(width: 3, height: 4, value: 0)
let red = Image<RGBA, Double>(width: 3, height: 5, color: Color(r: 1, g: 0, b: 0, a: 1))
```
### Input and output
For reading and writing image, Swim uses [`stb_image.h`](https://github.com/nothings/stb/blob/master/stb_image.h) and [`stb_image_write.h`](https://github.com/nothings/stb/blob/master/stb_image_write.h).
#### Reading & writing files
```swift
let image = try Image<RGBA, UInt8>(contentsOf: url)
try image.write(to: dstPath)
```
#### Reading & writing `Data`
```swift
let data = try Data(contentsOf: url)
let image = try Image<RGB, UInt8>(fileData: data)
let jpegData = try image.fileData(format: .jpeg(quality: 80))
```
### Platform specific operations
```swift
let image = try! Image<RGBA, UInt8>(contentsOf: url)
// on macOS
let nsImage = image.nsImage()
let imageFromNS = Image<RGBA, UInt8>(nsImage: nsImage)!
// on iOS
let uiImage = image.uiImage()
let imageFromUI = Image<RGBA, UInt8>(uiImage: uiImage)!
// with vImage
var argb = image.toARGB()
let kernel = Filter<UInt8>.mean(size: 5)
let blurred: Image<ARGB, UInt8> = try vImageUtils.createImageWithBuffer(width: argb.width, height: argb.height) { dest in
try vImageUtils.withBuffer(image: &argb) { argb in
try kernel.withUnsafeBufferPointer { kernel in
let flags: vImageProcessingFlag = [.edgeExtend,
.printDiagnosticsToConsole]
let code = vImageConvolve_ARGB8888(&argb, &dest, nil, 0, 0, kernel.baseAddress, 5, 5, nil, flags.vImage_Flags)
try vImageUtils.validateErrorCode(code)
}
}
}
// on Swift for TensorFlow
let tensor = Tensor(image: image)
```
### Subscriptions
#### Pixel manipulation
```swift
let image = try Image<RGBA, UInt8>(contentsOf: url)
let color: Color<RGBA, UInt8> = image[0, 0]
let red: UInt8 = image[0, 0, 0] // red channel of (x: 0, y: 0)
let red2: UInt8 = image[0, 0, .red] // ditto
let red3: UInt8 = image[0, 0][.red] // ditto
image[1, 0] += 1 // Add 1 for each channel
image[1, 0, .green] += 1 // Add 1 for Green channel
```
#### Subimage
```swift
let image = try Image<RGBA, UInt8>(contentsOf: url)
let sub1: Image<RGBA, UInt8> = image[0..<100, 0..<100]
let sub2: Image<RGBA, UInt8> = image[rows: 0..<100]
image[col: 2] += 1
```
#### Channel extraction
```swift
let image = try Image<RGBA, UInt8>(contentsOf: url)
let red: Image<Gray, UInt8> = image[channel: 0]
image[channel: .blue] += 1
```
### Conversion
```swift
let image = try Image<RGB, Float>(contentsOf: url)
// to gray scale
let gray1: Image<Gray, Float> = image.toGray() // with default weights
let gray2: Image<Gray, Float> = image.toGray(wr: 1/3, wg: 1/3, wb: 1/3) // with specified weights
// type conversion
let doubleImage1: Image<RGB, Double> = image.cast()
let doubleImage2 = image.cast(to: Double.self) // ditto
// pixel conversion
let redOnlyRGBA: Image<RGBA, Float> = image.pixelwiseConverted { src, dst in
dst[.red] = src[.red]
dst[.green] = 0
dst[.blue] = 0
dst[.alpha] = 1
}
```
### Drawing
```swift
var image = try Image<RGB, Float>(contentsOf: url)
image.drawLine((0, 0), (100, 120), color: Color(r: 1, g: 0, b: 0))
image.drawRect(10..<20, 30..<50, color: .green)
image.drawCircle(center: (50, 50), radius: 30, color: .blue)
image.drawImage(origin: (80, 80), rgbImage) // simply overwrites
image.drawImage(origin: (200, 200), rgbaImage) // with alpha blending
let font = try! TrueTypeFont(url: URL(fileURLWithPath: "/System/Library/Fonts/Helvetica.ttc"),
fontSize: 30)
image.drawText(origin: (100, 100),
text: "TEXT DRAWING",
font: font,
color: .black)
```
For font rendering, Swim uses [stb_truetype.h](https://github.com/nothings/stb/blob/master/stb_truetype.h).
### Resize
```swift
let image = try Image<RGB, Float>(contentsOf: url)
let resizedBL = image.resize(width: 512, height: 512) // default .bilinear
let resizedNN = image.resize(width: 512, height: 512, method: .nearestNeighbor)
let resizedBC = image.resize(width: 512, height: 512, method: .bicubic)
let resizedAA = image.resize(width: 512, height: 512, method: .areaAverage)
```
[Example: NearestNeighbor / Bilinear / Bicubic / Lanczos2 / Lanczos3 / Area Average](https://github.com/t-ae/swim/blob/99e7be2655057190b62426cdb85fe56b130d7126/Tests/VisualTests/ResizeVisualTests.swift#L29-L49)

### Warp
```swift
let image = try Image<RGBA, Double>(contentsOf: url)
let affine = AffineTransformation<Double>(scale: (1, 1.5), rotation: .pi/6. translation: (100, 120))
// `edgeMode` specifies how to fill pixels outside the base image.
let interpolator = BilinearInterpolator<RGBA, Double>(edgeMode: .edge)
let warpedImage = image.warp(transformation: affine, outputSize: (500, 500), interpolator: interpolator)
```
[Example: NN+Wrap / BL+Constant / BC+Reflect / Lanczos2+Edge / Lanczos3+Symmetric](https://github.com/t-ae/swim/blob/99e7be2655057190b62426cdb85fe56b130d7126/Tests/VisualTests/WarpVisualTests.swift#L216-L258)

### Compare images
```swift
let image1 = try Image<Gray, Double>(contentsOf: url1)
let image2 = try Image<Gray, Double>(contentsOf: url2)
let ssd = ImageCompare.ssd(image1, image2)
let sad = ImageCompare.sad(image1, image2)
let ncc = ImageCompare.ncc(image1, image2)
let zncc = ImageCompare.zncc(image1, image2)
let psnr = ImageCompare.psnr(image1, image2)
let ssim = ImageCompare.ssim(image1, image2, windowSize: 7)
```
### Blending
```swift
var bottomImage = try Image<RGB, Float>(contentsOf: url1)
let topimage = try Image<RGB, Float>(contentsOf: url2)
bottomImage(image: topImage, mode: .multiply)
bottomImage(image: topImage, mode: .additive)
bottomImage(image: topImage, mode: .screen)
bottomImage(image: topImage, mode: .overlay)
```
[Example: Multiply / Additive / Screen / Overlay](https://github.com/t-ae/swim/blob/99e7be2655057190b62426cdb85fe56b130d7126/Tests/VisualTests/BlendVisualTests.swift#L10-L25)

### Integral image
```swift
let image = try Image<Gray, Float>(contentsOf: url)
let integral = IntegralImageConverter.convert(image: image)
```
### Convolution/Filter
```swift
let image = try Image<Gray, Float>(contentsOf: url)
let blur = image.convoluted(Filter.gaussian3x3)
let maximum = image.rankFilter(.maximum, windowSize: 3)
let bilateral = image.bilateralFilter(windowSize: 5, distanceSigma: 1, valueSigma: 0.1)
let nlmean = image.nonLocalMeanFilter(windowSize: 5, distance: 2, sigma: 0.1)
```
[Example: Gaussian x10 / Bilateral x5 / Emboss / Sobel(Horizontal) / Laplacian](https://github.com/t-ae/swim/blob/99e7be2655057190b62426cdb85fe56b130d7126/Tests/VisualTests/FilterVisualTests.swift#L133-L163)

### Fast Fourier transformation
```swift
let image = try Image<Gray, Double>(contentsOf: url)
// image size must be power of 2
let transformed: Image<Gray, Complex<Double>> = FourierTransformer.fft(image: image)
let inverted: Image<Gray, Double> = FourierTransformer.ifft(image: transformed)
```
[Example: Spectrum and inverted image / Low-pass filtered / High-pass filtered](https://github.com/t-ae/swim/blob/1530980e40ea0ec3ab90d2e3feb0dc5d50700cc1/Tests/VisualTests/FourierTransformerVisualTests.swift#L54-L101)

### Histogram equalization
```swift
var image = try Image<Gray, Double>(contentsOf: url)
Histograms.equalize(image: &image)
```
[Example: Before / After](https://github.com/t-ae/swim/blob/fc1cac179d957f0ce0190ef8af9a50a3aa03c2da/Tests/VisualTests/HistogramsVisualTests.swift#L18-L36)

### Bayer filter
```swift
let image = try Image<RGB, Float>(contentsOf: url)
let converter = BayerConverter(pattern: .bggr)
let bayer = converter.convert(image: image)
let reconstruct = converter.demosaic(image: bayer)
```
[Example: Base / Bayer format / Reconstructed](https://github.com/t-ae/swim/blob/99e7be2655057190b62426cdb85fe56b130d7126/Tests/VisualTests/BayerVisualTests.swift#L12-L28)
Application examples
VisualTests contains more examples (works only on macOS).
License
Package Metadata
Repository: t-ae/swim
Default branch: master
README: readme.md