'Swift: The simpliest way to load a local png/jpg image file to a UInt8/UInt16 array? And write such array back to an image file?
I am translating to Swift some Python image processing tasks, which in Python only requires about 30 lines of code.
As I just want to obtain a simple command-line tool with no UI graphics, I'm trying to keep minimum dependence on Apple's high-level interface frameworks.
My Python code looks a bit like that:
from PIL import Image
import numpy
# Load a 16bit grayscale image and convert it to raw data array
img = Image.open("mylocaldir/Gray16.png")
(sizex,sizey) = img.size
inputpix = numpy.array(img.getdata()).astype(numpy.uint16).reshape((sizey,sizex))
# Here, do whatever processing to fill a RGB raw data array.
outputpix = numpy.zeros((sizey,sizex,3),numpy.uint8)
# ...
# ...
# ...
# Write the array back as a jpg file
img = Image.frombytes("RGB",(sizex,sizey),outputpix.reshape(sizex*sizey*3),"raw")
img.save("mylocaldir/OutputRGB.jpg")
Not so familiar with Apple's frameworks, I am struggling to figure out how to implement that in a way as simple as possible. Should a CGImage be used? Or is there any simpler object allowing image file I/O?
Could anybody help me getting the most streamlined Swift version of the Python code written above?
Solution 1:[1]
Here is what I came up for loading or saving png images from a UInt16 array of predefined size, only using CoreGraphics.
Sorry, as a Swift beginner with a C++ background, I might not organise my code in the best way!
func LoadPNG(pixels: inout [UInt16], sizx: Int, sizy: Int, name: String) -> Bool {
if FileManager.default.fileExists(atPath: name) {
let data : Data? = try? Data(contentsOf: URL(fileURLWithPath: name, isDirectory: false))
if data != nil {
if let cgprovider = CGDataProvider(data: data! as CFData) {
if let cgimg = CGImage(pngDataProviderSource: cgprovider, decode: nil, shouldInterpolate: false, intent: .defaultIntent) {
if (cgimg.width, cgimg.height, cgimg.bitsPerComponent, cgimg.bitsPerComponent) == (sizx, sizy, 16, 16) {
let cgspace = CGColorSpaceCreateDeviceGray()
let cgcontext = CGContext(data: &pixels, width: sizx, height: sizy, bitsPerComponent: cgimg.bitsPerComponent, bytesPerRow: cgimg.bytesPerRow, space: cgspace, bitmapInfo: cgimg.bitmapInfo.rawValue)
cgcontext?.draw(cgimg, in: CGRect(x: 0.0, y: 0.0, width: CGFloat(sizx), height: CGFloat(sizy)))
return true
}
}
}
}
}
return false
}
func SavePNG(pixels: inout [UInt16], sizx: Int, sizy: Int, name: String) -> Bool {
if let cgprovider = CGDataProvider(data: NSData(bytes:&pixels, length: MemoryLayout<UInt16>.size*pixels.count)) {
let cgspace = CGColorSpaceCreateDeviceGray()
let cginfo = CGBitmapInfo(rawValue:CGImageAlphaInfo.none.rawValue)
if let cgimg = CGImage(width: sizx, height: sizy, bitsPerComponent: 16, bitsPerPixel: 16, bytesPerRow: MemoryLayout<UInt16>.size*sizx, space: cgspace, bitmapInfo:cginfo, provider: cgprovider, decode: nil, shouldInterpolate: false, intent: .defaultIntent) {
let cfdata: CFMutableData = CFDataCreateMutable(nil, 0)
if let destination = CGImageDestinationCreateWithData(cfdata, kUTTypePNG as CFString, 1, nil) {
CGImageDestinationAddImage(destination, cgimg, nil)
if CGImageDestinationFinalize(destination) {
let data = cfdata as Data
try? data.write(to:URL(fileURLWithPath: name, isDirectory: false))
return true
}
}
}
}
return false
}
Unfortunately, there is a problem with this code: Repeated use will fill up the memory! Is there any resource that should have been manually released?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Develoop |
