I used this method to take a picture.
func convertImageFromCMSampleBufferRef(sampleBuffer:CMSampleBuffer) -> CIImage{
let pixelBuffer:CVPixelBufferRef = CMSampleBufferGetImageBuffer(sampleBuffer)!;
let ciImage:CIImage = CIImage(CVPixelBuffer: pixelBuffer)
if done == true {
newImage = UIImage(CIImage:ciImage, scale: CGFloat(1.0), orientation: .DownMirrored)
var imageData = UIImageJPEGRepresentation(newImage, 0.6)
var compressedJPGImage = UIImage(data: imageData)
UIImageWriteToSavedPhotosAlbum(compressedJPGImage!, nil, nil, nil)
}
return ciImage;
}
The code should work, but the variable imageData = nil
I tried converting the image in PNG, but with the same result.
with print
newImage = , {720, 1280} imageData = nil
You must convert CIImage to a CGImage, then CGImage to an UIImage, and then UIImage to NSData.
static let context = CIContext(options:nil);
let tempImage:CGImageRef = context.createCGImage(ciImage, fromRect: ciImage.extent())
let image = UIImage(CGImage: tempImage);
let imageData: NSData? = UIImageJPEGRepresentation(image, 0.6);
Related
I have a UIImage coming from server that I need to present in the UI as a monochromatic image with a given single color that can be an arbitrary as well. What's the best way to achieve it?
In my current method I am using following method that returns a monochromatic image for a given image and a color:
fileprivate func monochromaticImage(from image: UIImage, in color: UIColor) -> UIImage {
guard let img = CIImage(image: image) else {
return image
}
let color = CIColor(color: color)
guard let outputImage = CIFilter(name: "CIColorMonochrome",
withInputParameters: ["inputImage" : img,
"inputColor" : color])?.outputImage else {
return image
}
let context = CIContext()
if let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
let newImage = UIImage(cgImage: cgImage, scale: image.scale, orientation: image.imageOrientation)
return newImage
}
return image
}
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
}
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
}
I am having trouble taking 6 photos and storing them in an object array in order to animate them. I keep getting an error saying:
Array index out of range
Also, I realized that the "image" object isn't recognized outside the if-statement for some reason. What am I doing wrong?
func didPressTakePhoto(){
var picArray: [UIImage] = []
for index in 1...6 {
if let videoConnection = stillImageOutput?.connectionWithMediaType(AVMediaTypeVideo){
videoConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {
(sampleBuffer, error) in
if sampleBuffer != nil {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
let dataProvider = CGDataProviderCreateWithCFData(imageData)
let cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)
let image = UIImage(CGImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.Right)
self.tempImageView.image = image
self.tempImageView.hidden = false
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
picArray[index] = image;
}
})
}
}
self.gifView.animationImages = picArray;
self.gifView.animationDuration = 1.0
self.gifView.startAnimating()
}
picArray is empty, so you shouldn't use insert method. instead you have to use the append method.
The reason why is picArray is empty you are inserting the values inside the asynchronous block. The for loop completes before inserting images because it doesn't wait for asynchronous blocks to be complete.
You have to wait for asynchronous block to be complete before animating the image view.
You can achieve this using dispatch_group
func didPressTakePhoto(){
var picArray: [UIImage] = []
let dispatchGroup = dispatch_group_create()
for index in 1...6 {
dispatch_group_enter(dispatchGroup)
if let videoConnection = stillImageOutput?.connectionWithMediaType(AVMediaTypeVideo){
videoConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {
(sampleBuffer, error) in
if sampleBuffer != nil {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
let dataProvider = CGDataProviderCreateWithCFData(imageData)
let cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)
let image = UIImage(CGImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.Right)
self.tempImageView.image = image
self.tempImageView.hidden = false
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
picArray.append(image);
}
dispatch_group_leave(dispatchGroup)
})
} else {
dispatch_group_leave(dispatchGroup)
}
}
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue()) {
self.gifView.animationImages = picArray;
self.gifView.animationDuration = 1.0
self.gifView.startAnimating()
}
}
Hope this helps.
I am trying to generate QR Code using iOS Core Image API:
func createQRForString(#data : NSData)->CIImage!{
var qrFilter = CIFilter(name: "CIQRCodeGenerator")
qrFilter.setValue(data, forKey: "inputMessage")
qrFilter.setValue("H", forKey:"inputCorrectionLevel")
return qrFilter.outputImage
}
func createNonInterpolatedImageFromCIImage(image : CIImage,withScale scale:CGFloat)->UIImage{
let cgImage = CIContext(options: nil).createCGImage(image, fromRect: image.extent())
UIGraphicsBeginImageContext(CGSizeMake(image.extent().size.width*scale, image.extent().size.height*scale))
let context = UIGraphicsGetCurrentContext()
CGContextSetInterpolationQuality(context, kCGInterpolationNone)
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return scaledImage
}
And the following code in viewDidLoad method :
let data = "Hello World".dataUsingEncoding(NSUTF8StringEncoding)
if let image=createQRForString(data: data!){
let uiimage = createNonInterpolatedImageFromCIImage(image, withScale: 1.0)
imageView.image = uiimage
}
else{
println("Error loading image")
}
}
But it neither prints "Error" nor shows qr code in the imageView.
Here is the solution:
override func viewDidLoad() {
super.viewDidLoad()
self.imgView.image = generateCode()
}
func generateCode() -> UIImage {
let filter = CIFilter(name: "CIQRCodeGenerator")
let data = "Hello World".dataUsingEncoding(NSUTF8StringEncoding)
filter.setValue("H", forKey:"inputCorrectionLevel")
filter.setValue(data, forKey:"inputMessage")
let outputImage = filter.outputImage
let context = CIContext(options:nil)
let cgImage = context.createCGImage(outputImage, fromRect:outputImage.extent())
let image = UIImage(CGImage:cgImage, scale:1.0, orientation:UIImageOrientation.Up)
let resized = resizeImage(image!, withQuality:kCGInterpolationNone, rate:5.0)
return resized
}
func resizeImage(image: UIImage, withQuality quality: CGInterpolationQuality, rate: CGFloat) -> UIImage {
let width = image.size.width * rate
let height = image.size.height * rate
UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, height), true, 0)
let context = UIGraphicsGetCurrentContext()
CGContextSetInterpolationQuality(context, quality)
image.drawInRect(CGRectMake(0, 0, width, height))
let resized = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resized;
}