Masking an image in Swift 2.0 - swift

I have searched everywhere and found a few examples on how to mask an image, but none of them seem to work for me... I am running on iOS9 and using Swift2.0
Right now this is what I have:
class func maskImage(background: UIImage, withMask mask: UIImage) -> UIImage {
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue).rawValue
let colorSpace = CGColorSpaceCreateDeviceRGB()!
let context = CGBitmapContextCreate(nil, CGImageGetWidth(mask.CGImage), CGImageGetHeight(mask.CGImage), 8, 0, colorSpace, bitmapInfo)
CGContextDrawImage(context, CGRectMake(0, 0, mask.size.width * mask.scale, mask.size.height * mask.scale), mask.CGImage)
let maskRef: CGImageRef = CGBitmapContextCreateImage(context)!
let masked: CGImageRef = CGImageCreateWithMask(background.CGImage, maskRef)!
let icon: UIImage = UIImage(CGImage: masked, scale: mask.scale, orientation: mask.imageOrientation)
return icon
}
I am passing two pngs in and getting the error: EXC_BAD_INSTRUCTION on the line where I initialize masked. I don't have enough rep to post the image.
I am probably missing something super simple, but would love your help. Thank you!

With Swift 3.0 we have had the great name change....
so this appears to function now after some renaming... and cleaning...
based on this example: ( https://www.innofied.com/implementing-image-masking-in-ios/ )
func maskImage(image:UIImage, mask:UIImage )->UIImage{
let imageReference = (image.cgImage)!
let maskReference = (mask.cgImage)!
let imageMask = CGImage.init(
maskWidth: maskReference.width
,height: maskReference.height
,bitsPerComponent: maskReference.bitsPerComponent
,bitsPerPixel: maskReference.bitsPerPixel
,bytesPerRow: maskReference.bytesPerRow
,provider: maskReference.dataProvider!
,decode: nil
,shouldInterpolate: true
)
return (UIImage(cgImage:(imageReference.masking(imageMask!))!))
}

I am also running the latest versions and had a lot of trouble with this. I was finally able to use the below code to crop part of an image and overlay a puzzle piece with an image mask.
import UIKit
import CoreGraphics
class ViewController2: UIViewController {
#IBOutlet var imageView: UIImageView!
var p1PosX: CGFloat = 0.0
var p1PosY: CGFloat = 0.0
var p1Width: CGFloat = 0.0
var p1Height: CGFloat = 0.0
override func viewDidLoad() {
super.viewDidLoad()
let panda = UIImage(named: "panda.jpeg")!
let puzzle1 = UIImage(named: "PP-4-1.gif")!
//Crop
let contextSize: CGSize = panda.size
let rect = CGRect(x: 0, y: 0, width: 200, height: 200)
p1PosX = 0
p1PosY = 0
p1Width = contextSize.width / 2
p1Height = contextSize.height / 2
let rect1: CGRect = CGRectMake(p1PosX, p1PosY, 350, 350)
let imageRef1: CGImageRef = CGImageCreateWithImageInRect(panda.CGImage, rect1)!
let panda1: UIImage = UIImage(CGImage: imageRef1, scale: panda.scale, orientation: panda.imageOrientation)
imageView.image = panda1
//Mask
UIGraphicsBeginImageContext(imageView.frame.size)
panda1.drawInRect(rect)
puzzle1.drawInRect(CGRectMake(0, 0, imageView.frame.size.width , imageView.frame.size.height), blendMode: CGBlendMode.DestinationIn, alpha: 1.0)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
imageView.image = newImage
}
}

In case anybody stumbles across this same situation and is looking for a clean solution, here's a function to mask an image. It works for .jpg, .png and .gif images.
func maskImage(image:UIImage, mask:(UIImage))->UIImage{
let imageReference = image.CGImage
let maskReference = mask.CGImage
let imageMask = CGImageMaskCreate(CGImageGetWidth(maskReference),
CGImageGetHeight(maskReference),
CGImageGetBitsPerComponent(maskReference),
CGImageGetBitsPerPixel(maskReference),
CGImageGetBytesPerRow(maskReference),
CGImageGetDataProvider(maskReference), nil, true)
let maskedReference = CGImageCreateWithMask(imageReference, imageMask)
let maskedImage = UIImage(CGImage:maskedReference!)
return maskedImage
}
Add the following declaration lines in your viewDidLoad() or init() as needed :
let image = UIImage(named: "image.png")
let maskingImage = UIImage(named: "mask.png")
let imageView = UIImageView(image: maskingImage)
imageView.image = maskImage(image!, mask: maskingImage!)
Thanks to Manish Kumar's solution Implementing Image Masking in iOS

Related

How to implement CICheckerboardGenerator in swift 4?

#IBOutlet var imageView: UIImageView!
var context: CIContext!
var currentFilter: CIFilter!
override func viewDidLoad() {
super.viewDidLoad()
context = CIContext()
currentFilter = CIFilter(name: "CICheckerboardGenerator", parameters: ["inputColor0" : CIColor.white, "inputColor1" : CIColor.black, "inputCenter" : CIVector(x: 0, y: 0), "inputWidth" : 50.00])
if let cgimg = context.createCGImage(currentFilter.outputImage!, from: currentFilter.outputImage!.extent) {
let processedImage = UIImage(cgImage: cgimg)
imageView.image = processedImage
}
}
I have created two variables at the top of the class and in viewDidLoad() function trying to generate the checkerboard. What am I doing wrong? I know this filter does not require an input image. It does not create an image as I would expect it to do.
You need to create the CGImage as below,
if let cgimg = context.createCGImage(currentFilter.outputImage!, from: self.imageView.bounds) {
self.imageView.image = UIImage(cgImage: cgimg)
}
I believe the main issue in the initial question was the size of the resulting image: currentFilter.outputImage!.extent.
The image generated by CICheckerboardGenerator is basically an infinite plane (to the extent of available CGFloat values). In order to use the resulting CIImage you need to crop it:
if let pattern = currentFilter.outputImage {
let image = pattern.cropped(to: self.imageView.bounds)
self.imageView.image = UIImage(ciImage: image)
}
This way you don't need neither CIContext nor intermediate CGImage

change resolution and size of image with cocoa/osx/swift (no mobile apps)

I try to change the size and the resolution of an image programmatically, afterwards I save this image.
The imagesize in the imageView is changing, but when I look at my file "file3.png" it always has the original resolution of 640x1142.
I googled around but can't find a solution. I try to redraw the image. But maybe it's the wrong strategy.
thanks
#IBAction func pickOneImageBtn(sender: AnyObject) {
//load image from path
pickedImage.image = loadImageFromPath(fileInDocumentsDirectory("Angebote.png"))
let newSize = NSSize(width: 10, height: 10)
if let image = pickedImage.image {
print("found image")
//cast to CGImage
var imageRect:CGRect = CGRectMake(0, 0, image.size.width, image.size.height)
let imageRef = image.CGImageForProposedRect(&imageRect, context: nil, hints: nil)
if let imageRefExists = imageRef {
print("Cast to CGImage worked \(imageRefExists)")
}
//redraw to NSImage with new size
let imageWithNewSize = NSImage(CGImage: imageRef!, size: newSize)
//save on disk
let imgData: NSData! = imageWithNewSize.TIFFRepresentation!
let bitmap: NSBitmapImageRep! = NSBitmapImageRep(data: imgData!)
if let pngCoverImage = bitmap!.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:]) {
pngCoverImage.writeToFile("/...correctpath.../imageSourceForResize/file3.png", atomically: false)
print("saved new image")
}
//the size is smaller
pickedImage.image = imageWithNewSize
}
}
Change
let imgData: NSData! = pickedImage.image!.TIFFRepresentation!
to
let imgData: NSData! = imageWithNewSize.TIFFRepresentation!
I tried to change the size of a NSImage for Mac application and here is the working function to resize an image written in swift.
func resize(image: NSImage, w: Int, h: Int) -> NSImage
{
let destSize = NSMakeSize(CGFloat(w), CGFloat(h))
let newImage = NSImage(size: destSize)
newImage.lockFocus()
image.drawInRect(NSMakeRect(0, 0, destSize.width, destSize.height), fromRect: NSZeroRect, operation: NSCompositingOperation.CompositeCopy, fraction: 1.0)
newImage.unlockFocus()
newImage.size = destSize
return NSImage(data: newImage.TIFFRepresentation!)!
}
You need to pass 3 parameters to call this function i.e NSImage, width, height and this function will return resized image.
targetimage = resize(source, w: Int(targetwidth), h: Int(targetheight))

Cropping a captured still image

I want just a specific part of my captured image to be cropped in a circular shape right before it's viewed.
Here is the code for capturing still images:
#IBAction func takePhoto(sender: UIButton) {
self.stillImageOutput!.captureStillImageAsynchronouslyFromConnection(self.stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo)) { (buffer:CMSampleBuffer!, error:NSError!) -> Void in
var image = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer)
var data_image = UIImage(data: image)
self.previewImage.image = data_image
}
}
Thank you
if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(buffer) {
if let image = UIImage(data: imageData) {
let square = image.size.width < image.size.height ? CGSize(width: image.size.width, height: image.size.width) : CGSize(width: image.size.height, height: image.size.height)
let imageView = UIImageView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: square))
imageView.contentMode = UIViewContentMode.ScaleAspectFill
imageView.image = image
imageView.layer.cornerRadius = square.width/2
imageView.layer.masksToBounds = true
UIGraphicsBeginImageContext(imageView.bounds.size)
imageView.layer.renderInContext(UIGraphicsGetCurrentContext())
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.previewImage.image = result
}
}

Asynchronous function causing crashing

I'm generating a QR Code to put into a UIImage. I'm running the generation function asynchronously but for some reason the app crashes when I run it on my phone, but doesn't crash in the simulator. I'm not really sure what's going on... Any ideas?
Setup Image
let QR = UIImageView()
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) { // 1
var img = self.generateQRImage(self.arr[sender.tag],withSizeRate: self.screenWidth-40)
dispatch_async(dispatch_get_main_queue()) { // 2
QR.image = img
}
}
QR.frame = CGRectMake(0,0,screenWidth-40,screenWidth-40)
QR.center = CGPoint(x:screenWidth/2,y:screenHeight/2)
sView.addSubview(QR)
Generate QR
func generateQRImage(stringQR:NSString, withSizeRate rate:CGFloat) -> UIImage
{
var filter:CIFilter = CIFilter(name:"CIQRCodeGenerator")
filter.setDefaults()
var data:NSData = stringQR.dataUsingEncoding(NSUTF8StringEncoding)!
filter.setValue(data, forKey: "inputMessage")
var outputImg:CIImage = filter.outputImage
var context:CIContext = CIContext(options: nil)
var cgimg:CGImageRef = context.createCGImage(outputImg, fromRect: outputImg.extent())
var img:UIImage = UIImage(CGImage: cgimg, scale: 1.0, orientation: UIImageOrientation.Up)!
var width = img.size.width * rate
var height = img.size.height * rate
UIGraphicsBeginImageContext(CGSizeMake(width, height))
var cgContxt:CGContextRef = UIGraphicsGetCurrentContext()
CGContextSetInterpolationQuality(cgContxt, kCGInterpolationNone)
img.drawInRect(CGRectMake(0, 0, width, height))
img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
The intent of withSizeRate is clearly to be a scaling factor to apply to the QR image (which is 27x27). But you are using the screen width as the multiplier. That results in an exceedingly large image (once it's uncompressed, used in image view; don't go by the size of the resulting JPEG/PNG file). The theoretical internal, uncompressed representation of this image is extremely large (300 mb on iPhone 6 and nearly 400 mb on iPhone 6+). When I ran it through the iPhone 6 simulator, memory usage actually spiked to 2.4 gb:
I would suggest using a smaller scaling factor. Or just create an image that is precisely the size of the imageview (though use zero for the scale with UIGraphicsBeginImageContextWithOptions).
For example, you could simply pass the CGSize of the image view to generateQRImage, and adjust the method like so:
func generateQRImage(stringQR: String, size: CGSize) -> UIImage {
let filter = CIFilter(name:"CIQRCodeGenerator")
filter.setDefaults()
let data = stringQR.dataUsingEncoding(NSUTF8StringEncoding)!
filter.setValue(data, forKey: "inputMessage")
let outputImage = filter.outputImage
let context = CIContext(options: nil)
let cgImage = context.createCGImage(outputImage, fromRect: outputImage.extent())
var image = UIImage(CGImage: cgImage, scale: 1.0, orientation: UIImageOrientation.Up)!
let width = size.width
let height = size.height
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), true, 0)
let cgContext = UIGraphicsGetCurrentContext()
CGContextSetInterpolationQuality(cgContext, kCGInterpolationNone)
image.drawInRect(CGRectMake(0, 0, width, height))
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}

Saving an image on top of another image in Swift

I am learning Swift and I am creating an app that uses a personal photo and puts another on top of it. I now have a hacky solution, to create a screenshot of the area and save it. I need to do this in Swift
#IBAction func saveImage(sender: AnyObject) {
//Create the UIImage
UIGraphicsBeginImageContext(imageView.frame.size)
view.layer.renderInContext(UIGraphicsGetCurrentContext())
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//Save it to the camera roll
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
But, this was working and isn't anymore. But, this is also not the best solution.
So guys, how can I save an image to the camera roll from a personal image, with an image as overlay?
Help would be greatly appreciated!! Thanks!
I would recommend reading through this thread. All your answers are there. Once you read through that article, the following code sample should help you composite the two images together properly.
func saveImage() {
let bottomImage = UIImage(named: "bottom")!
let topImage = UIImage(named: "top")!
let newSize = CGSizeMake(100, 100) // set this to what you need
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
bottomImage.drawInRect(CGRect(origin: CGPointZero, size: newSize))
topImage.drawInRect(CGRect(origin: CGPointZero, size: newSize))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
Hopefully this gets you going in the right direction.
Apple advises against UIGraphicsBeginImageContext, so as long as your app does not support devices older than iOS 10, then use something like this:
private func drawLogoIn(_ image: UIImage, _ logo: UIImage, position: CGPoint) -> UIImage {
let renderer = UIGraphicsImageRenderer(size: image.size)
return renderer.image { context in
image.draw(in: CGRect(origin: CGPoint.zero, size: image.size))
logo.draw(in: CGRect(origin: position, size: logo.size))
}
}
Besides performance gains, you get full P3 range.
UPDATE FOR SWIFT 4
func saveImage() {
let bottomImage = UIImage(named: "your bottom image name")!
let topImage = UIImage(named: "your top image name")!
let newSize = CGSize(width: 100, height: 100) // set this to what you need
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
bottomImage.draw(in: CGRect(origin: CGPoint.zero, size: newSize))
topImage.draw(in: CGRect(origin: CGPoint.zero, size: newSize))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
To use the image just refer to newImage
EXAMPLE HOW TO USE THE IMAGE:
#IBOutlet weak var imageButton: UIButton!
imageButton.setBackgroundImage(newImage), for: .normal)
This is an edit of cnoon's answer but optimized for Swift 4.
Updated to Swift 3.0:
func saveImage() {
let bottomImage = UIImage(named: "bottom")!
let topImage = UIImage(named: "top")!
let newSize = CGSizeMake(100, 100) // set this to what you need
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
bottomImage.draw(in: CGRect(origin: CGPointZero, size: newSize))//As drawInRect is deprecated
topImage.draw(at: CGRect(origin: CGPointZero, size: newSize))//As drawInRect is deprecated
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}