Skip to content

t-ae/swim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

About

Cross platform image library for Swift

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •