Swiftpack.co - t-ae/swim as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
t-ae/swim
Cross platform image library for Swift
.package(url: "https://github.com/t-ae/swim.git", from: "3.9.0")

Swim

Cross platform image library for Swift.

API

Generic Image type

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

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 and stb_image_write.h.

Reading & writing files

let image = try Image<RGBA, UInt8>(contentsOf: url)
try image.write(to: dstPath)

Reading & writing Data

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

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

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

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

let image = try Image<RGBA, UInt8>(contentsOf: url)
let red: Image<Gray, UInt8> = image[channel: 0]
image[channel: .blue] += 1

Conversion

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

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.

Resize

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

resize

Warp

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

warp

Compare images

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

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

blend

Integral image

let image = try Image<Gray, Float>(contentsOf: url)
let integral = IntegralImageConverter.convert(image: image)

Convolution/Filter

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

filter

Fast Fourier transformation

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

fft

Histogram equalization

var image = try Image<Gray, Double>(contentsOf: url)
Histograms.equalize(image: &image)

Example: Before / After

hist

Bayer filter

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

bayer_bggr

Application examples

VisualTests contains more examples (works only on macOS).

License

The MIT License

GitHub

link
Stars: 39
Last commit: 1 year ago

Ad: Job Offers

iOS Software Engineer @ Perry Street Software
Perry Street Software is Jack’d and SCRUFF. We are two of the world’s largest gay, bi, trans and queer social dating apps on iOS and Android. Our brands reach more than 20 million members worldwide so members can connect, meet and express themselves on a platform that prioritizes privacy and security. We invest heavily into SwiftUI and using Swift Packages to modularize the codebase.

Submit a free job ad (while I'm testing this). The analytics numbers for this website are here.

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics