Unable to convert CIImage to UIImage in Swift 3.0 - ios

I am making image form QR Code by using following code:
func createQRFromString(str: String) -> CIImage? {
let stringData = str.dataUsingEncoding(NSUTF8StringEncoding)
let filter = CIFilter(name: "CIQRCodeGenerator")
filter?.setValue(stringData, forKey: "inputMessage")
filter?.setValue("H", forKey: "inputCorrectionLevel")
return filter?.outputImage
}
And Then I am adding to UIImageView Like this:
if let img = createQRFromString(strQRData) {
let somImage = UIImage(CIImage: img, scale: 1.0, orientation: UIImageOrientation.Down)
imgviewQRcode.image = somImage
}
Now I need to save this to a JPEG or PNG file. But when I am doing so my app crashes:
#IBAction func btnSave(sender: AnyObject) {
// // Define the specific path, image name
let documentsDirectoryURL = try! NSFileManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
// create a name for your image
let fileURL = documentsDirectoryURL.URLByAppendingPathComponent("image.jpg")
if let image = imgviewQRcode.image // imgviewQRcode is UIImageView
{
if let path = fileURL?.path
{
if !NSFileManager.defaultManager().fileExistsAtPath(fileURL!.path!)
{
if UIImageJPEGRepresentation(image, 1.0)!.writeToFile(path, atomically: true)
{
print("file saved")
}
}//Checking existing file
}//Checking path
}//CHecking image
}
Crash Point
UIImageJPEGRepresentation(image, 1.0)!.writeToFile(path, atomically: true)
Reason
fatal error: unexpectedly found nil while unwrapping an Optional value
Debug Tests:

func convert(cmage:CIImage) -> UIImage
{
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(cmage, from: cmage.extent)!
let image:UIImage = UIImage.init(cgImage: cgImage)
return image
}
Use this function to convert CIImage to UIImage . It works .

func convert(image:CIImage) -> UIImage
{
let image:UIImage = UIImage.init(ciImage: image)
return image
}
Perhaps, this was unavailable before, but it is now possible to create UIImages directly from CIImage.

My final code
func generateQRCode(from string: String) -> UIImage? {
let data = string.data(using: String.Encoding.ascii)
if let filter = CIFilter(name: "CIQRCodeGenerator") {
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 3, y: 3)
if let output = filter.outputImage?.transformed(by: transform) {
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(output, from: output.extent)!
let image:UIImage = UIImage.init(cgImage: cgImage)
return image
}
}
return nil
}

Related

Using Core Image Filters in ios - cannot save resulting image - swift

I am applying a number of core image filters to an image that I have captured. The problem that I am facing is that once I have applied the filters and saved the resulting image, I am unable to save it. I do not get any errors in my code but when I go to retrieve the image the application is unable to locate it. It says that the file does not exist. I suspect I may have used the filters incorrectly some how.
// PROCESS IMAGE TO MAKE IT LOOK 'SCANNED'
guard let scannedImage = self.processImage(image: image)else{
print("failed to process image")
return
}
// SAVE SCANNED IMAGE TO LOCAL FILE SYSTEM
self.saveImageToLocalFile(image: scannedImage) // PROBELM HERE.
// PROCESS IMAGE
func processImage(image: UIImage) -> UIImage? {
guard let ciimage = CIImage.init(image: image) else{return nil}
// APPLY FILTERS
guard let shadowCimage = shadow(inputImage: ciimage) else{return nil}
guard let colorControlCimage = colorControl(input: shadowCimage, contrast: 4.0, saturation: 0.3, brightness: 0.3) else{return nil}
guard let sharpenCimage = sharpen(inputImage: colorControlCimage, inputRadius: 2.5, inputIntensity: 1.0) else {return nil}
guard let sharpIntensity = sharpenLumin(inputImg: sharpenCimage, inputSharpness: 0.2) else{return nil}
print("processImage END")
return UIImage.init(ciImage: sharpIntensity)
}
// FILTER 'CIColorControls'
func colorControl(input: CIImage, contrast: Float, saturation: Float, brightness: Float) -> CIImage? {
let filter = CIFilter(name: "CIColorControls")
filter?.setValue(input, forKey: kCIInputImageKey)
filter?.setValue(contrast, forKey: kCIInputContrastKey)
filter?.setValue(saturation, forKey: kCIInputSaturationKey)
filter?.setValue(brightness, forKey: kCIInputBrightnessKey)
return filter?.outputImage
}
// FILTER 'CIUnsharpMask'
func sharpen(inputImage: CIImage, inputRadius: Float, inputIntensity: Float) -> CIImage? {
let filter = CIFilter(name: "CIUnsharpMask")
filter?.setValue(inputImage, forKey: kCIInputImageKey)
//filter?.setDefaults()
filter?.setValue(inputRadius, forKey: kCIInputRadiusKey)
filter?.setValue(inputIntensity, forKey: kCIInputIntensityKey)
return filter?.outputImage
}
// FILTER 'CIHighlightShadowAdjust'
func shadow(inputImage: CIImage) -> CIImage? {
let filter = CIFilter(name: "CIHighlightShadowAdjust")
filter?.setValue(inputImage, forKey: kCIInputImageKey)
filter?.setDefaults()
return filter?.outputImage
}
// FILTER 'CISharpenLuminance'
func sharpenLumin(inputImg: CIImage, inputSharpness: Float) -> CIImage? {
let filter = CIFilter.init(name: "CISharpenLuminance")
filter?.setValue(inputImg, forKey: kCIInputImageKey)
filter?.setValue(inputSharpness, forKey: kCIInputSharpnessKey)
return filter?.outputImage
}
// SAVE UIIMAGE TO LOCAL FILE SYSTEM
func saveImageToLocalFile(image: UIImage) -> Void {
// CREATE URL - SAVE TO PATH
let imageURL = createPhotoURL() // CORRECT FULL LENGTH URL FOR FILE UPLAOD
print("IMAGE SAVED TO URL: \(imageURL)")
let imageData = UIImageJPEGRepresentation(image, 1.0)
do{
try imageData?.write(to: imageURL)
self.scannedImageURL = imageURL
}catch{
print("error writing img to local dir")
}
}
The problem is that your imageData is nil, because of how you are creating the UIImage.
As explained in this answer, you have a nil in the UIImage.cgImage property, which UIImageJPEGRepresentation uses. Here's the code you need to use:
func saveImageToLocalFile(image: UIImage) -> Void {
let imageURL = createPhotoURL()
var uiImage = image
if image.cgImage == nil {
guard let ciImage = image.ciImage, let cgImage = CIContext(options: nil).createCGImage(ciImage, from: ciImage.extent) else { return }
uiImage = UIImage(cgImage: cgImage)
}
if let imageData = UIImageJPEGRepresentation(uiImage, 1.0) {
do {
try imageData?.write(to: imageURL)
self.scannedImageURL = imageURL
} catch {
print("error writing img to local dir")
}
} else {
print("could not create JPEG image")
}
}

UIImageJPEGRepresentation returns nil (on a device - OK on Simulator)

I get nil on an iOS device (works ok on Simulator or Playground) when I create create a barcode UIImage with generateBarcode("ABCDEF") and call UIImageJPEGRepresentation or UIImagePNGRepresentation.
There is clearly something still wrong in the UIImage as the debugger cannot display the UIImage either. The image exists, the cgImage property is set but UIImageJPEGRepresentation doesn't like it.
I have tried to solve it as per: UIImageJPEGRepresentation returns nil
class func generateBarcode(from string: String) -> UIImage? {
let data = string.data(using: String.Encoding.ascii)
if let filter = CIFilter(name: "CICode128BarcodeGenerator") {
filter.setValue(data, forKey: "inputMessage")
if let ciImage = filter.outputImage {
if let cgImange = convertCIImageToCGImage(inputImage: ciImage) {
return UIImage(cgImage: cgImange)
}
}
}
return nil
}
class func convertCIImageToCGImage(inputImage: CIImage) -> CGImage? {
let context = CIContext(options: nil)
if let cgImage = context.createCGImage(inputImage, from: inputImage.extent) {
return cgImage
}
return nil
}

Using CIImage initial report EXC_BAD_INSTRUCTION error in extension - swift

Before I asking this question, I have searched the related post:
"unrecognized selector" when attempting to access CIFilter's outputImage
I don't know if is because of using swift or extension, I will get error. I have tested two methods to get the CIImage, but fails in EXC_BAD_INSTRUCTION:
Attention
my url is not http:// prefix, but weixin://wxpay/bizpayurl?pr=ZwBVaW0, and I think this is not the reason of the error.
Method one:
extension String {
func initQRImage() ->UIImage {
let filter:CIFilter = CIFilter.init(name: "CIQRCodeGenerator")!
filter.setDefaults()
let data:Data = self.data(using: String.Encoding.utf8)!
filter.setValue(data, forKey: "inputMessage")
let outputImage:CGImage = filter.outputImage as! CGImage // EXC_BAD_INSTRUCTION here
let qr_image = UIImage.init(cgImage: outputImage)
return qr_image
}
}
Method two:
extension String {
func initQRImage() ->UIImage {
let url:URL = URL.init(fileURLWithPath: self)
let inputImage:CIImage = CIImage.init(contentsOf: url)! // EXC_BAD_INSTRUCTION here
let filter: CIFilter = CIFilter.init(name: "CIAreaAverage")!
filter.setValue(inputImage, forKey: kCIInputImageKey)
let inputExtent:CGRect = inputImage.extent
let extent:CIVector = CIVector.init(x: inputExtent.origin.x, y: inputExtent.origin.y, z: inputExtent.size.width, w: inputExtent.size.height)
filter.setValue(extent, forKey: kCIInputExtentKey)
let outputImage:CIImage = filter.value(forKey: "outputImage") as! CIImage
let qr_image = UIImage.init(cgImage: outputImage as! CGImage)
return qr_image
}
}
Two method will report EXC_BAD_INSTRUCTION error here, you can see the annotation I write after the report error line.
EDIT - 1
I have tried in my project again, not using extension, there is the error too, and data is not nil:
I think the data is nil.
let data:Data = self.data(using: String.Encoding.utf8)!
Also an UIImage instantiated with CIImage has no bitmap, it has no actual image, it's just a set of instructions for applying a filter. So your methods to convert to UIImage shouldn't work.
Finally I found a outdated method to generate QR code, after my improvement, it becomes this:
// quality can modify the defintion
class func generateQRImage(stringQR:NSString, withSizeRate rate:CGFloat, quality:CGFloat?) -> UIImage
{
let filter:CIFilter = CIFilter(name:"CIQRCodeGenerator")!
filter.setDefaults()
let data:NSData = stringQR.data(using: String.Encoding.utf8.rawValue)! as NSData
filter.setValue(data, forKey: "inputMessage")
let outputImg:CIImage = filter.outputImage!
let context:CIContext = CIContext(options: nil)
var tmp_quality = quality
if quality == nil {
tmp_quality = 1.0
}
let transform: CGAffineTransform = CGAffineTransform(scaleX: tmp_quality!, y: tmp_quality!);
let outputImg_after = outputImg.applying(transform)
let cgimg:CGImage = context.createCGImage(outputImg_after, from: outputImg_after.extent)!
var img:UIImage = UIImage(cgImage: cgimg, scale: 1.0, orientation: UIImageOrientation.up)
let width = img.size.width * rate
let height = img.size.height * rate
UIGraphicsBeginImageContext(CGSize.init(width: width, height: height))
let cgContxt:CGContext = UIGraphicsGetCurrentContext()!
cgContxt.interpolationQuality = .high // cgContxt kCGInterpolationNone
img.draw(in: CGRect.init(x: 0, y: 0, width: width, height: height)) // (0, 0, width, height)
img = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return img
}

How to generate a EAN13 barcode?

I'm trying to create a simple EAN13 image to show a barcode from a String.
I tried with this code but it can only generate a code128. What can I use to generate a EAN13?
class Barcode {
class func fromString(string : String) -> UIImage? {
let data = string.dataUsingEncoding(NSASCIIStringEncoding)
let filter = CIFilter(name: "CICode128BarcodeGenerator")
filter.setValue(data, forKey: "inputMessage")
return UIImage(CIImage: filter.outputImage)
}
}
let img = Barcode.fromString("1234567890123")
you can try this EAN13BarcodeGenerator
Usage is pretty simple:
BarCodeView *barCodeView = [[BarCodeView alloc] initWithFrame:kBarCodeFrame];
[self.view addSubview:barCodeView];
[barCodeView setBarCode:GetNewRandomEAN13BarCode()];
my two cents for osx..
func barCodeFromString(string : String, destSize: NSSize) -> NSImage? {
let data = string.data(using: .ascii)
guard let filter = CIFilter(name: "CICode128BarcodeGenerator") else{
return nil
}
filter.setValue(data, forKey: "inputMessage")
guard let ciImage : CIImage = filter.outputImage else{
return nil
}
let c_size = ciImage.extent.size
let w_ratio = destSize.width/c_size.width
let h_ratio = destSize.height/c_size.height
let ratio = w_ratio>h_ratio ? h_ratio : w_ratio
let transform = CGAffineTransform(scaleX: ratio, y: ratio)
let scaled = ciImage.transformed(by: transform)
let rep = NSCIImageRep(ciImage: scaled)
let nsImage = NSImage(size: rep.size)
nsImage.addRepresentation(rep)
return nsImage
}

How can I generate a barcode from a string in Swift?

I am a new iOS developer. I was wondering how can I generate a barcode in Swift.
I have the code already, there are multiple resources from where to learn how to read a barcode, but I didn't find any that talks about generating one from a string.
Thanks a lot!
P.S. I know there is a similar question about this, but it's for Objective-C. I don't know Obj-C and I find it difficult coming from .NET.
You could use a CoreImage (import CoreImage) filter to do that!
class Barcode {
class func fromString(string : String) -> UIImage? {
let data = string.data(using: .ascii)
if let filter = CIFilter(name: "CICode128BarcodeGenerator") {
filter.setValue(data, forKey: "inputMessage")
if let outputCIImage = filter.outputImage {
return UIImage(ciImage: outputCIImage)
}
}
return nil
}
}
let img = Barcode.fromString("whateva")
A newer version, with guard and failable initialiser:
extension UIImage {
convenience init?(barcode: String) {
let data = barcode.data(using: .ascii)
guard let filter = CIFilter(name: "CICode128BarcodeGenerator") else {
return nil
}
filter.setValue(data, forKey: "inputMessage")
guard let ciImage = filter.outputImage else {
return nil
}
self.init(ciImage: ciImage)
}
}
Usage:
let barcode = UIImage(barcode: "some text") // yields UIImage?
According to the docs :
Generates an output image representing the input data according to the
ISO/IEC 15417:2007 standard. The width of each module (vertical line)
of the barcode in the output image is one pixel. The height of the
barcode is 32 pixels. To create a barcode from a string or URL,
convert it to an NSData object using the NSASCIIStringEncoding string
encoding.
Improved code:
Barcode scaling
Set barcode image margin
Convert the UIImage to NSData (for some reason it wasn't possible with the code above).
It won't fail when sharing the barcode image (probably because of the same bug)
Swift 3
func generateBarcode(from string: String) -> UIImage? {
let data = string.data(using: String.Encoding.ascii)
if let filter = CIFilter(name: "CICode128BarcodeGenerator") {
filter.setDefaults()
//Margin
filter.setValue(7.00, forKey: "inputQuietSpace")
filter.setValue(data, forKey: "inputMessage")
//Scaling
let transform = CGAffineTransform(scaleX: 3, y: 3)
if let output = filter.outputImage?.applying(transform) {
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(output, from: output.extent)!
let rawImage:UIImage = UIImage.init(cgImage: cgImage)
//Refinement code to allow conversion to NSData or share UIImage. Code here:
//http://stackoverflow.com/questions/2240395/uiimage-created-from-cgimageref-fails-with-uiimagepngrepresentation
let cgimage: CGImage = (rawImage.cgImage)!
let cropZone = CGRect(x: 0, y: 0, width: Int(rawImage.size.width), height: Int(rawImage.size.height))
let cWidth: size_t = size_t(cropZone.size.width)
let cHeight: size_t = size_t(cropZone.size.height)
let bitsPerComponent: size_t = cgimage.bitsPerComponent
//THE OPERATIONS ORDER COULD BE FLIPPED, ALTHOUGH, IT DOESN'T AFFECT THE RESULT
let bytesPerRow = (cgimage.bytesPerRow) / (cgimage.width * cWidth)
let context2: CGContext = CGContext(data: nil, width: cWidth, height: cHeight, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: cgimage.bitmapInfo.rawValue)!
context2.draw(cgimage, in: cropZone)
let result: CGImage = context2.makeImage()!
let finalImage = UIImage(cgImage: result)
return finalImage
}
}
return nil
}
If your deployment target is at least iOS 8, you can use Core Image. Here is my BarcodeGenerator class (you need to import CoreImage):
class BarcodeGenerator {
enum Descriptor: String {
case code128 = "CICode128BarcodeGenerator"
case pdf417 = "CIPDF417BarcodeGenerator"
case aztec = "CIAztecCodeGenerator"
case qr = "CIQRCodeGenerator"
}
class func generate(from string: String,
descriptor: Descriptor,
size: CGSize) -> CIImage? {
let filterName = descriptor.rawValue
guard let data = string.data(using: .ascii),
let filter = CIFilter(name: filterName) else {
return nil
}
filter.setValue(data, forKey: "inputMessage")
guard let image = filter.outputImage else {
return nil
}
let imageSize = image.extent.size
let transform = CGAffineTransform(scaleX: size.width / imageSize.width,
y: size.height / imageSize.height)
let scaledImage = image.transformed(by: transform)
return scaledImage
}
}
It can be used like this
BarcodeGenerator.generate(from: "barcode-string",
descriptor: .code128,
size: CGSize(width: 800, height: 300))
Use like this,
func createBarcodeFromString(barcode:String)->UIImage?{
let data = self.data(using: .isoLatin1)
guard let filter = CIFilter(name: "CICode128BarcodeGenerator") else {
return nil
}
filter.setValue(data, forKey: "inputMessage")
filter.setValue(7.0, forKey:"inputQuietSpace")
guard var ciImage = filter.outputImage else {
return nil
}
let imageSize = ciImage.extent.integral
let outputSize = CGSize(width:320, height: 60)
ciImage = ciImage.transformed(by:CGAffineTransform(scaleX: outputSize.width/imageSize.width, y: outputSize.height/imageSize.height))
let image = convertCIImageToUIImage(ciimage: ciImage)
return image
}
func convertCIImageToUIImage(ciimage:CIImage)->UIImage{
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(ciimage, from: ciimage.extent)!
let image:UIImage = UIImage.init(cgImage: cgImage)
return image
}

Resources