Swift5 Macos Imageresize Memory Issue

Swift5 MacOS ImageResize memory issue

You can put your whole code that it is inside your loop inside an autoreleasepool:

If you write a loop that creates many temporary objects. You may use
an autorelease pool block inside the loop to dispose of those objects
before the next iteration. Using an autorelease pool block in the loop
helps to reduce the maximum memory footprint of the application.

for i in paths.indices {
autoreleasepool {
// all your image resizing code goes here
}
}

How to easily resize/optimize an image size with iOS?

A couple of suggestions are provided as answers to this question. I had suggested the technique described in this post, with the relevant code:

+ (UIImage*)imageWithImage:(UIImage*)image 
scaledToSize:(CGSize)newSize;
{
UIGraphicsBeginImageContext( newSize );
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return newImage;
}

As far as storage of the image, the fastest image format to use with the iPhone is PNG, because it has optimizations for that format. However, if you want to store these images as JPEGs, you can take your UIImage and do the following:

NSData *dataForJPEGFile = UIImageJPEGRepresentation(theImage, 0.6);

This creates an NSData instance containing the raw bytes for a JPEG image at a 60% quality setting. The contents of that NSData instance can then be written to disk or cached in memory.

Cocoa: Capture Screen and scale image on saving in Swift

First, you can't resize using CGImageDestinationCreateWithURL or CGImageDestinationAddImage. If you look at the docs here and here you will notice that neither kCGImagePropertyPixelWidth or kCGImagePropertyPixelHeight is supported.

You will need to resize manually. You can use this tool, or modify it, if you find it helpful. It supports fill (stretch) and fit (scale while keeping the original aspect ratio) content modes. If you specify .fit it will center the drawing in the resulting image. If you specify .fill it will fill the whole space stretching whichever dimension it needs to.

enum ImageResizer {

enum ContentMode {
case fill
case fit
}

enum Error: Swift.Error {
case badOriginal
case resizeFailed
}

static func resize(_ source: CGImage, to targetSize: CGSize, mode: ContentMode) throws -> CGImage {

let context = CGContext(
data: nil,
width: Int(targetSize.width),
height: Int(targetSize.height),
bitsPerComponent: source.bitsPerComponent,
bytesPerRow: 0,
space: source.colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!,
bitmapInfo: source.bitmapInfo.rawValue
)

guard let context = context else {
throw Error.badOriginal
}

let drawingSize: CGSize
switch mode {
case .fill:
drawingSize = targetSize
case .fit:
drawingSize = CGSize(width: source.width, height: source.height)
.scaledToFit(target: targetSize)
}

let drawRect = CGRect(origin: .zero, size: targetSize)
.makeCenteredRect(withSize: drawingSize)

context.interpolationQuality = .high
context.draw(source, in: drawRect)

guard let result = context.makeImage() else {
throw Error.resizeFailed
}

return result
}
}

ImageResizer depends on these CG extensions for scaling the source image and centering scaled image:

extension CGSize {

var maxDimension: CGFloat {
Swift.max(width, height)
}

var minDimension: CGFloat {
Swift.min(width, height)
}

func scaled(by scalar: CGFloat) -> CGSize {
CGSize(width: width * scalar, height: height * scalar)
}

func scaleFactors(to target: CGSize) -> CGSize {
CGSize(
width: target.width / width,
height: target.height / height
)
}

func scaledToFit(target: CGSize) -> CGSize {
return scaled(by: scaleFactors(to: target).minDimension)
}
}

extension CGRect {
func makeCenteredRect(withSize size: CGSize) -> CGRect {
let origin = CGPoint(
x: midX - size.width / 2.0,
y: midY - size.height / 2.0
)
return CGRect(origin: origin, size: size)
}
}

Also, make sure you set up permissions if you're going to save to .downloadsDirectory.

Loading/Downloading image from URL on Swift

Xcode 8 or later • Swift 3 or later

Synchronously:

if let filePath = Bundle.main.path(forResource: "imageName", ofType: "jpg"), let image = UIImage(contentsOfFile: filePath) {
imageView.contentMode = .scaleAspectFit
imageView.image = image
}

Asynchronously:

Create a method with a completion handler to get the image data from your url

func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}

Create a method to download the image (start the task)

func downloadImage(from url: URL) {
print("Download Started")
getData(from: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
// always update the UI from the main thread
DispatchQueue.main.async() { [weak self] in
self?.imageView.image = UIImage(data: data)
}
}
}

Usage:

override func viewDidLoad() {
super.viewDidLoad()
print("Begin of code")
let url = URL(string: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")!
downloadImage(from: url)
print("End of code. The image will continue downloading in the background and it will be loaded when it ends.")
}

Extension:

extension UIImageView {
func downloaded(from url: URL, contentMode mode: ContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { [weak self] in
self?.image = image
}
}.resume()
}
func downloaded(from link: String, contentMode mode: ContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloaded(from: url, contentMode: mode)
}
}

Usage:

imageView.downloaded(from: "https://cdn.arstechnica.net/wp-content/uploads/2018/06/macOS-Mojave-Dynamic-Wallpaper-transition.jpg")

How do I resize the UIImage to reduce upload image size

Swift 5.4 & Xcode 13

I was not satisfied with the solutions here, which generate an image based on a given KB size, since most of them used .jpegData(compressionQuality: x). This method won't work with large images, since even with compression quality set to 0.0, the large image will remain large, e.g. a 10 MB produced by portrait mode of a newer iPhone still will be above 1 MB with compressionQuality set to 0.0.

Therefore I used some answers here and rewrote a Helper Struct which converts an image in a background que:

import UIKit

struct ImageCompressor {
static func compress(image: UIImage, maxByte: Int,
completion: @escaping (UIImage?) -> ()) {
DispatchQueue.global(qos: .userInitiated).async {
guard let currentImageSize = image.jpegData(compressionQuality: 1.0)?.count else {
return completion(nil)
}

var iterationImage: UIImage? = image
var iterationImageSize = currentImageSize
var iterationCompression: CGFloat = 1.0

while iterationImageSize > maxByte && iterationCompression > 0.01 {
let percantageDecrease = getPercantageToDecreaseTo(forDataCount: iterationImageSize)

let canvasSize = CGSize(width: image.size.width * iterationCompression,
height: image.size.height * iterationCompression)
UIGraphicsBeginImageContextWithOptions(canvasSize, false, image.scale)
defer { UIGraphicsEndImageContext() }
image.draw(in: CGRect(origin: .zero, size: canvasSize))
iterationImage = UIGraphicsGetImageFromCurrentImageContext()

guard let newImageSize = iterationImage?.jpegData(compressionQuality: 1.0)?.count else {
return completion(nil)
}
iterationImageSize = newImageSize
iterationCompression -= percantageDecrease
}
completion(iterationImage)
}
}

private static func getPercantageToDecreaseTo(forDataCount dataCount: Int) -> CGFloat {
switch dataCount {
case 0..<3000000: return 0.05
case 3000000..<10000000: return 0.1
default: return 0.2
}
}
}

Compress an image to max 1 MB:

        ImageCompressor.compress(image: image, maxByte: 1000000) { image in
guard let compressedImage = image else { return }
// Use compressedImage
}
}

iOS Swift: How to properly scale down an image?

You can resize the image with any size once you have a valid UIImage:

func resizedImageWith(image: UIImage, targetSize: CGSize) -> UIImage {

let imageSize = image.size
let newWidth = targetSize.width / image.size.width
let newHeight = targetSize.height / image.size.height
var newSize: CGSize

if(newWidth > newHeight) {
newSize = CGSizeMake(imageSize.width * newHeight, imageSize.height * newHeight)
} else {
newSize = CGSizeMake(imageSize.width * newWidth, imageSize.height * newWidth)
}

let rect = CGRectMake(0, 0, newSize.width, newSize.height)

UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)

image.drawInRect(rect)

let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

return newImage
}

For any other queries/modifications you have, refer this NShipster

How to reduce filter image size in iOS

To reduce the image size you can use:

newImage = UIImageJPEGRepresentation(image: UIImage, compressionQuality: CGfloat)


Related Topics



Leave a reply



Submit