Capturing a preview image with AVCaptureStillImageOutput - ios

Before stackoverflow members answer with "You shouldn't. It's a privacy violation" let me counter with why there is a legitimate need for this.
I have a scenario where a user can change the camera device by swiping left and right. In order to make this animation not look like absolute crap, I need to grab a freeze frame before making this animation.
The only sane answer I have seen is capturing the buffer of AVCaptureVideoDataOutput, which is fine, but now I can't let the user take the video/photo with kCVPixelFormatType_420YpCbCr8BiPlanarFullRange which is a nightmare trying to get a CGImage from with CGBitmapContextCreate See How to convert a kCVPixelFormatType_420YpCbCr8BiPlanarFullRange buffer to UIImage in iOS
When capturing a still photo are there any serious quality considerations when using AVCaptureVideoDataOutput instead of AVCaptureStillImageOutput? Since the user will be taking both video and still photos (not just freeze-frame preview stills) Also, can some one "Explain it to me like I'm five" about the differences between kCVPixelFormatType_420YpCbCr8BiPlanarFullRange/kCVPixelFormatType_32BGRA besides one doesn't work on old hardware?

I don't think there is a way to directly capture a preview image using AVFoundation. You could however take a capture the preview layer by doing the following:
UIGraphicsBeginImageContext(previewView.frame.size);
[previewLayer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Where previewView.layer is the
previewLayer is the AVCaptureVideoPreviewLayer added to the previewView. "image" is rendered from this layer and can be used for your animation.

Related

Manipulate CVPixelbuffer, vImage, CGImage and UIImage for a rotation

I get from AR session, the current frame with: self.sceneView.session.currentFrame?.capturedImage so I get a CVPixelBuffer with my image information.
I followed this link to convert my CVPixelBuffer to CGImage. (I use previously createCGImage method but this method create a memory leak in my app.)
But I have an issue now, the CGImage received at the end is on the landscape mode and not portrait (my app allows only portrait mode) like you can see on the image below.
So I'm trying to fix this rotation.
I saw vImageRotate90_ARGB8888(_:_:_:_:_:) exist but I have trouble to use it because my pointer for the backColor is always nil.
Do you have any method to rotate it without a memory leak because I have to rotate it couple times per seconde?
Thanks

How to add overlay on AVCaptureVideoPreviewLayer?

I am building an iOS app using Swift which requires QR code scanner functionality.
I have implemented a QR code scanner using AVFoundation, right now my capture screen looks same as a video recording screen i.e. AVCaptureVideoPreviewLayer shows what is being captured by the camera.
But since it is a QR code scanner and not a regular image or video capture, I would like my VideoPreviewLayer to look like this:
I understand this can be achieved by adding another VideoPreviewLayer on top of one VideoPreviewLayer.
My questions are:
How do I add the borders only to the edges in the upper (or smaller) preview layer?
How do I change the brightness level for the VideoPreviewLayer in the background?
How to ignore media captured by the the background layer?
You shouldn't use another VideoPreviewLayer. Instead you should add two sublayers - one for the masked background area and one for the corners.
Have a look at the source code in this repo for an example.
To limit the video capturing to the masked area you have to set the rectOfInterest of your AVCaptureMetadataOutput.
let rectOfInterest = videoPreviewLayer.metadataOutputRectConverted(fromLayerRect: rect)
metadataOutput.rectOfInterest = rectOfInterest
Long story short: you can use AVCaptureVideoPreviewLayer for video capturing, create another CALayer() and use layer.insertSublayer(..., above: ...) to insert your "custom" layer above the video layer, and by custom I mean just yet another CALayer with let say
layer.contents = spinner.cgImage
Here's a bit more detailed instructions

Programmatic Screenshot of Video Layer with UIKit/UIGraphics

I am currently dealing with an issue associated to UIKit/UIGraphics in Swift (library also existed in ObjC UIKit/UIGraphics).
I am programmatically capturing a UIView to save it locally. The simple code goes like this:
UIGraphicsBeginImageContextWithOptions(view.frame.size, false, UIScreen.mainScreen().scale)
view.layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// use image var
However, this code does not capture anything else on the screen like a video player. The video player ends up being a black frame and not rendering in the image context.
Native iOS clearly does this. You can screenshot video easily.
What's the solution here? Thanks for your help.

Take a snapshot of a Group element with WatchKit & save to camera roll

I'm trying to programmatically take a snapshot of a Group element and its contents (a text label) and save it to the camera roll.
I would usually do something like this (see code snippet) for an iPhone app but with WatchKit the Group element has a different structure to a UIView.
Any help would be much appreciated. Here's my code:
// grab reference to the area you'd like to capture
WKInterfaceGroup *theArea = _theGroup;
// define the size and grab a UIImage from it
UIGraphicsBeginImageContextWithOptions(theArea.bounds.size, theArea.opaque, 0.0);
[theArea.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *screengrab = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// save screengrab to Camera Roll
UIImageWriteToSavedPhotosAlbum(screengrab, nil, nil, nil);
Unfortunately, there are no snapshot-style methods in the current version of WatchKit, so techniques like the one you've mentioned are not available. In fact, there are very few properties that can be read from a WatchKit interface.
The only way you'll be able to do something like this is if you render the group as an image in your WatchKit extension, then use that image to populate a WKInterfaceImage (or appropriate background image) on the Watch. Of course, this means that you have to create custom drawing code that mimics the look of the Watch controls. Depending on the complexity of your interface, this could take a lot of work.

How do you get an UIImage from an AVCaptureVideoPreviewLayer?

I have already tried this solution CGImage (or UIImage) from a CALayer
However I do not get anything.
Like the question says, I am trying to get an UIImage from the preview layer of the camera. I know I can either capture a still image or use the outputsamplebuffer but my session quality video is set to photo so either of these 2 aproaches are slow and will give me a big image.
So what I thought could work is to get the image directly from the preview layer, since this has exactly the size I need and the operations have already been made on it. I just dont know how to get this layer to draw into my context so that I can get it as an UIImage.
Perhaps another solution would be to use OpenGL to get this layer directly as a texture?
Any help will be appreciated, thanks.
Quoting Apple from this Technical Q&A:
A: Starting from iOS 7, the UIView class provides a method
-drawViewHierarchyInRect:afterScreenUpdates:, which lets you render a snapshot of the complete view hierarchy as visible onscreen into a
bitmap context. On iOS 6 and earlier, how to capture a view's drawing
contents depends on the underlying drawing technique. This new method
-drawViewHierarchyInRect:afterScreenUpdates: enables you to capture the contents of the receiver view and its subviews to an image
regardless of the drawing techniques (for example UIKit, Quartz,
OpenGL ES, SpriteKit, AV Foundation, etc) in which the views are
rendered
In my experience regarding AVFoundation is not like that, if you use that method on view that host a preview layer you will only obtain the content of the view without the image of the preview layer. Using the -snapshotViewAfterScreenUpdates: will return a UIView that host a special layer. If you try to make an image from that view you won't see nothing.
The only solution I know are AVCaptureVideoDataOutput and AVCaptureStillImageOutput. Each one has its own limit. The first one can't work simultaneously with a AVCaptureMovieFileOutput acquisition, the latter makes the shutter noise.

Resources