Swift - Image Data From CIImage QR Code / How to render CIFilter Output
You have a couple of issues in your code. You need to convert your string to data using String Encoding isoLatin1 before passing it to the filter. Another issue is that to convert your CIImage to data you need to redraw/render your CIImage and to prevent blurring the image when scaled you need to apply a transform to the image to increase its size:
extension StringProtocol {
var qrCode: UIImage? {
guard
let data = data(using: .isoLatin1),
let outputImage = CIFilter(name: "CIQRCodeGenerator",
parameters: ["inputMessage": data, "inputCorrectionLevel": "M"])?.outputImage
else { return nil }
let size = outputImage.extent.integral
let output = CGSize(width: 250, height: 250)
let format = UIGraphicsImageRendererFormat()
format.scale = UIScreen.main.scale
return UIGraphicsImageRenderer(size: output, format: format).image { _ in outputImage
.transformed(by: .init(scaleX: output.width/size.width, y: output.height/size.height))
.image
.draw(in: .init(origin: .zero, size: output))
}
}
}
extension CIImage {
var image: UIImage { .init(ciImage: self) }
}
Playground testing:
let link = "https://stackoverflow.com/questions/51178573/swift-image-data-from-ciimage-qr-code-how-to-render-cifilter-output?noredirect=1"
let image = link.qrCode!
let data = image.jpegData(compressionQuality: 1) // 154785 bytes
Given a CIImage, what is the fastest way to write image data to disk?
I would suggest passing the CGImage directly to ImageIO using CGImageDestination. You can pass a dictionary to CGImageDestinationAddImage to indicate the compression quality, image orientation, etc.
CFDataRef save_cgimage_to_jpeg (CGImageRef image)
{
CFMutableDataRef cfdata = CFDataCreateMutable(nil,0);
CGImageDestinationRef dest = CGImageDestinationCreateWithData(data, CFSTR("public.jpeg"), 1, NULL);
CGImageDestinationAddImage(dest, image, NULL);
if(!CGImageDestinationFinalize(dest))
; // error
CFRelease(dest);
return cfdata
}
iOS 7 Core Image QR Code generation too blur
I was about to start bounty on this question but i found the answer.
What you need is a scale filter. To achieve this with CoreImage, you need to do something like this:
CIImage *input = [CIImage imageWithCGImage: ImageView.Image.CGImage]; // input image is 100 X 100
CGAffineTransform transform = CGAffineTransformMakeScale(5.0f, 5.0f); // Scale by 5 times along both dimensions
CIImage *output = [input imageByApplyingTransform: transform];
// output image is now 500 X 500
FROM THIS SO ANSWER: https://stackoverflow.com/a/16316701/2859764
CIFilter output image nil
You cannot call UIImage(CIImage:)
and use that UIImage as the image of a UIImageView. UIImageView requires a UIImage backed by a bitmap (CGImage). A UIImage instantiated with CIImage has no bitmap; it has no actual image, it's just a set of instructions for applying a filter. That is why your UIImageView's image is nil.
imageView.image = UIImage(ciImage: ) won't update a second time
Quick searching ... seems to be either a "bug" or a change...
Changing your generator code to this seems to correct the issue:
func generateQRCode(from string: String) -> UIImage? {
let data = string.data(using: String.Encoding.ascii)
let context = CIContext()
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) {
if let retImg = context.createCGImage(output, from: output.extent) {
return UIImage(cgImage: retImg)
}
}
}
return nil
}
func generateBarcode(from string: String) -> UIImage? {
let data = string.data(using: String.Encoding.ascii)
let context = CIContext()
if let filter = CIFilter(name: "CICode128BarcodeGenerator") {
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 3, y: 3)
if let output = filter.outputImage?.transformed(by: transform) {
if let retImg = context.createCGImage(output, from: output.extent) {
return UIImage(cgImage: retImg)
}
}
}
return nil
}
How to remove the gray Frame from QR code generator
Using the extension from the link I posted as a starting point:
extension String {
func qrCode(background: UIColor = .white, color: UIColor = .black, output: CGSize = CGSize(width: 250, height: 250))-> UIImage? {
guard
let data = data(using: .isoLatin1),
let filter = CIFilter(name: "CIQRCodeGenerator")
else { return nil }
filter.setValue(data, forKey: "inputMessage")
filter.setValue("M", forKey: "inputCorrectionLevel")
guard let image = filter.outputImage
else { return nil }
let size = image.extent.integral
let matrix = CGAffineTransform(scaleX: output.width / size.width, y: output.height / size.height)
UIGraphicsBeginImageContextWithOptions(output, false, 0)
defer { UIGraphicsEndImageContext() }
guard
let colorFilter = CIFilter(name: "CIFalseColor",
parameters: ["inputImage" : image.transformed(by: matrix),
"inputColor1": CIColor(color: background) ,
"inputColor0": CIColor(color: color)]),
let coloredImage = colorFilter.outputImage
else { return nil }
UIGraphicsBeginImageContextWithOptions(output, false, 0)
defer { UIGraphicsEndImageContext() }
UIImage(ciImage: coloredImage).draw(in: CGRect(origin: .zero, size: output))
return UIGraphicsGetImageFromCurrentImageContext()
}
}
let link = "https://stackoverflow.com/questions/51178573/swift-image-data-from-ciimage-qr-code-how-to-render-cifilter-output?noredirect=1"
if let coloredQRCode = link.qrCode(color: .red, output: CGSize(width: 500, height: 500)) {
coloredQRCode
}
How can I convert an UIImage to grayscale in Swift using CIFilter?
You can use CIColorControls and set Contrast Key kCIInputContrastKey
to increase the black/white contrast as follow:
Xcode 9 • Swift 4
extension String {
static let colorControls = "CIColorControls"
}
extension UIImage {
var coreImage: CIImage? { return CIImage(image: self) }
}
extension CIImage {
var uiImage: UIImage? { return UIImage(ciImage: self) }
func applying(contrast value: NSNumber) -> CIImage? {
return applyingFilter(.colorControls, parameters: [kCIInputContrastKey: value])
}
func renderedImage() -> UIImage? {
guard let image = uiImage else { return nil }
return UIGraphicsImageRenderer(size: image.size,
format: image.imageRendererFormat).image { _ in
image.draw(in: CGRect(origin: .zero, size: image.size))
}
}
}
let url = URL(string: "https://i.stack.imgur.com/Xs4RX.jpg")!
do {
if let coreImage = UIImage(data: try Data(contentsOf: url))?.coreImage,
let increasedContrast = coreImage.applying(contrast: 1.5) {
imageView.image = increasedContrast.uiImage
// if you need to convert your image to data (JPEG/PNG) you would need to render the ciimage using renderedImage method on CIImage
}
} catch {
print(error)
}
To convert from colors to grayscale you can set the Saturation Key kCIInputSaturationKey
to zero:
extension CIImage {
func applying(saturation value: NSNumber) -> CIImage? {
return applyingFilter(.colorControls, parameters: [kCIInputSaturationKey: value])
}
var grayscale: CIImage? { return applying(saturation: 0) }
}
let url = URL(string: "https://i.stack.imgur.com/Xs4RX.jpg")!
do {
if let coreImage = UIImage(data: try Data(contentsOf: url))?.coreImage,
let grayscale = coreImage.grayscale {
// use grayscale image here
imageView.image = grayscale.uiImage
}
} catch {
print(error)
}
Related Topics
How to Remove the Xcode Warning Apple MACh-O Linker Warning 'Pointer Not Aligned at Address
Scale Image in an Uibutton to Aspectfit
iOS Convert Large Numbers to Smaller Format
Trouble with Autolayout on Uitableviewcell
Swift: How to Get Substring from Start to Last Index of Character
Xcode 6 with Swift Super Slow Typing and Autocompletion
How to Save Nsmutablearray in Nsuserdefaults
What Tool(S) How to Use to Produce iPhone App Screencasts
Uiscrollview with Centered Uiimageview, Like Photos App
Linking Objective-C Categories in a Static Library
Add Shadow on Uiview Using Swift 3
How to Hide Tab Bar with Animation in iOS
Make Custom Button on Tab Bar Rounded
Build Not Visible in Itunes Connect
Simulator Slow-Motion Animations Are Now On
Convert Uiimage to Nsdata and Convert Back to Uiimage in Swift