Using UIImageOrientation to Save UIImage Correctly in Landscape Mode - iphone

I am receiving my UIImage's from AVCaptureSession and setting them into UIImageView's. The portrait mode sets images with correct orientation but once I use landscape mode the images are set rotated 90 degrees clockwise.
I did a little research and discovered that I can use UIImageOrientation with this method to resolve my issue:
[UIImage imageWithCGImage:cgimageref scale:1 orientation:orientation]
But I do not understand how. If I look at UIImageOrientation on all images, the value is always 3 (NSLog(#"%d",UIImageOrientation)) in both portrait and landscape modes. I am puzzled about how this can help me. Am I missing something?
Here is orientation code:
-(void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
self.prevLayer.frame = self.view.bounds;
self.prevLayer.orientation = [[UIDevice currentDevice] orientation];
}
shouldAutorotateToInterfaceOrientation returns YES.

The solution took some time to find. I had to add orientation to AVCaptureConnection
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in stillImageOutput.connections){
for (AVCaptureInputPort *port in [connection inputPorts]){
if ([[port mediaType] isEqual:AVMediaTypeVideo] ){
videoConnection = connection;
break;
}
}
if (videoConnection) { break; }
}
This line below is the fix:
if([videoConnection isVideoOrientationSupported]) {
[videoConnection setVideoOrientation:[[UIDevice currentDevice] orientation]];
}
And all images work right with any orientation.

Related

ZBar SDK orientation Issue in Ipad?

I have an IPAD application which is supporting only in landscape mode. In which i wanted to use ZbarSDK for reading bar codes,i created a view in my view controller then load it with background of the readerviewcontroller.working fine in iphone.But in ipad with this orientations it is behaving strage,like loading in portrait mode,etc.I am using this but no luck.reader.supportedOrientationsMask = ZBarOrientationMask(UIInterfaceOrientationLandscapeRightcan anybody help me how to use this sdk correctly in ipad with landscape orientations?
I have just had problems with the embedded reader view and read that a call to setNeedsLayout was missing on rotation, not sure if it will help someone but here is what fixed my problems
- (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) orient duration: (NSTimeInterval) duration
{
[self.readerView willRotateToInterfaceOrientation:orient duration:duration];
[self.readerView setNeedsLayout];
}
i think use bellow code may this help you...
ZBarReaderViewController *reader = [ZBarReaderViewController new];
[reader shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeRight];
hope,this help you..
:)
Works fine for me. Our iPad app supports only Landscape orientation:
// ADD: present a barcode reader that scans from the camera feed
ZBarReaderViewController *reader = [ZBarReaderViewController new];
reader.readerDelegate = self;
ScanOverlay overlayController = [[ScanOverlay alloc] initWithNibName:#"ScanOverlay" bundle:nil];
reader.cameraOverlayView = overlayController.view;
ZBarImageScanner *scanner = reader.scanner;
// TODO: (optional) additional reader configuration here
reader.supportedOrientationsMask = UIInterfaceOrientationMaskLandscape;
reader.wantsFullScreenLayout = YES;
reader.showsZBarControls = NO; //If we don't set this to NO, the overlay may not display at all
reader.tracksSymbols = YES;
[overlayController willRotateToInterfaceOrientation:YES duration:0.5];
// ADD: present a barcode reader that scans from the camera feed
// EXAMPLE: disable rarely used I2/5 to improve performance
[scanner setSymbology: ZBAR_I25
config: ZBAR_CFG_ENABLE
to: 0];
//Show the scanner view
if([self respondsToSelector:#selector(presentViewController:animated:completion:)]){
[self presentViewController:reader animated:YES completion:^(void){}];
} else if([self respondsToSelector:#selector(presentModalViewController:animated:)]) {
[self presentModalViewController:reader animated:YES];
} else {
NSLog(#"Error! Can't present the View Controller");
}

Always seeing Mirror image while capturing from Front Camera iOS 5.0

While capturing data from Front camera I am always getting Mirror image, how can I get what I am seeing in my preview window. I have set videoMirrored to be TRUE. Following is the code snippet:
AVCaptureConnection *lConnection = nil;
for ( AVCaptureConnection *connection in [lHandle->m_output connections]) {
for ( AVCaptureInputPort *port in [connection inputPorts] ) {
if ( [[port mediaType] isEqual:AVMediaTypeVideo] ) {
lConnection = connection;
break;
}
}
}
if ([lConnection isVideoOrientationSupported])
[lConnection setVideoOrientation:AVCaptureVideoOrientationPortrait];
if ([lConnection isVideoMirroringSupported])
[lConnection setVideoMirrored:TRUE];
Changing setVideoMirrored to True/False also doesn't change anything
(isVideoMirroringSupported returns Success)
Flip the mirrored image with the following code.
UIImage * flippedImage = [UIImage imageWithCGImage:picture.CGImage scale:picture.scale orientation:UIImageOrientationLeftMirrored];
Swift version:
let flippedImage = UIImage(CGImage: image.CGImage, scale: image.scale, orientation: .LeftMirrored)
You can use the withHorizontallyFlippedOrientation() to flip an UIImage.
// first get an UIImage from the front camera
let sourceImg = UIImage(cgImage: image, scale: 1.0, orientation: .leftMirrored)
return sourceImg.withHorizontallyFlippedOrientation()

No perfect way to detect device orientation on iPad?

EDIT: The given answer works on the device, but beware it fails on the simulator.
When my iPad starts up, I show a loading label, centered in the middle of the screen. I set its autoresizingMask so it recenters on orientation change.
As the app starts up, the label's text changes, so I want to recenter the label based on its new length. However, the following piece of code doesn't center the label correctly:
- (void) setLabelText:(NSString*)text {
CGSize maximumLabelSize = CGSizeMake(500,20);
CGSize expectedLabelSize = [text sizeWithFont:loadingLabel.font
constrainedToSize:maximumLabelSize
lineBreakMode:loadingLabel.lineBreakMode];
loadingLabel.frame = CGRectMake(self.view.frame.size.width/2-expectedLabelSize.width/2,
loadingLabel.frame.origin.y,
expectedLabelSize.width,
loadingLabel.frame.size.height);
loadingLabel.text = text;
}
I also considered checking [[UIDevice currentDevice]orientation], and if the iPad is in landscape mode, then I'd use self.view.frame.size.height to set the xOrigin of the label.
However, if the device is face up or face down, (and not landscape or portrait) then this method fails. I also have a lastOrientation variable in my appDelegate, which remembers if the app is in landscape or portrait, even when face up or face down, based on the device's last known orientation. However, at start-up, this variable isn't necessarily set.
Is there some simple solution I am missing here, so I can resize and center my label?
EDIT: I tried checking UIStatusBarOrientation based on the advice posted, but it doesn't work:
if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft
|| [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight) {
NSLog(#"landscape");
width = self.view.frame.size.height;
} else {
NSLog(#"portrait");
}
This always logs portrait, at least on start-up, on the simulator.
Check [[UIApplication sharedApplication] statusBarOrientation]
I found a trick to solve the FaceUp orientation issue!!!
Delay the orientation check till AFTER the app has started running, then set variables, view sizes, etc.!!!
//CODE
- (void)viewDidLoad {
[super viewDidLoad];
//DELAY
[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:#selector(delayedCheck)
userInfo:nil
repeats:NO];
}
-(void)delayedCheck{
//DETERMINE ORIENTATION
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait ){
FACING = #"PU";
}
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortraitUpsideDown ){
FACING = #"PD";
}
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeLeft ){
FACING = #"LL";
}
if( [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationLandscapeRight ){
FACING = #"LR";
}
//DETERMINE ORIENTATION
//START
[self setStuff];
//START
}
-(void)setStuff{
if( FACING == #"PU" ){
//logic for Portrait
}
else
if( FACING == #"PD" ){
//logic for PortraitUpsideDown
}
else{
if( FACING == #"LL"){
//logic for LandscapeLeft
}
else
if( FACING == #"LR" ){
//logic for LandscapeRight
}
}
//CODE
You can addSubviews, position elements, etc. in the 'setStuff' function ... anything that would initially depend on the orientation!!!
:D
-Chris Allinson

Code works in sim, but only sometimes on device

The code to set image to imageView is:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
SDWebImageManager *manager = [SDWebImageManager sharedManager];
UIImage *cachedImage = [manager imageWithURL:_url];
if (cachedImage)
{
_imageView.image = cachedImage;
[spinner stopAnimating];
}
else
{
[spinner startAnimating];
[manager downloadWithURL:_url delegate:self];
}
// Configure the view for the selected state
[super setSelected:selected animated:animated];
}
- (void)webImageManager:(SDWebImageManager *)imageManager
didFinishWithImage:(UIImage *)_image
{
[spinner stopAnimating];
_imageView.image = _image;
[self setNeedsLayout];
}
I use SDWebImage
this works every time in simulator, but when I run the app in the device (Ipod touch)
80 % of the images is just black, but if I go to another view and back the images are set (from cache).
I have try to set delay on _imageView = _image, but change.
(Answered in a question edit. Converted to a community wiki answer. See Question with no answers, but issue solved in the comments (or extended in chat) )
The OP wrote:
Now I have try the code on a Iphone 3gs and it works perfect, can understand why it wont work on ipod touch.
When i Compiled and Run as "Release" it works on ipod, but not in as debugger. Wierd..

Why AVCaptureSession output a wrong orientation?

So, I followed Apple's instructions to capture video session using AVCaptureSession: http://developer.apple.com/iphone/library/qa/qa2010/qa1702.html. One problem I'm facing is that even though the orientation of the camera / iPhone device is vertical (and the AVCaptureVideoPreviewLayer shows a vertical camera stream), the output image seems to be in the landscape mode. I checked the width and height of imageBuffer inside imageFromSampleBuffer: of the sample code, and I got 640px and 480px respectively. Does anyone know why this's the case?
Thanks!
Take a look at the header AVCaptureSession.h. There is a definition for an enum called AVCaptureVideoOrientation that defines various video orientations. On the AVCaptureConnection object there is a property called videoOrientation that is a AVCaptureVideoOrientation. You should be able to set this to change the orientation of the video. You probably want AVCaptureVideoOrientationLandscapeRight or AVCaptureVideoOrientationLandscapeLeft.
You can find the AVCaptureConnections for the session by looking at the outputs for the session. The outputs have a connections property that is an array of connections for that output.
Y'all are making this difficult.
In the DidOutputSampleBuffer, simply change the orientation before you grab the image. It's mono, but you have
public class OutputRecorder : AVCaptureVideoDataOutputSampleBufferDelegate {
public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
{
try {
connection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft;
in objC it's this method
- ( void ) captureOutput: ( AVCaptureOutput * ) captureOutput
didOutputSampleBuffer: ( CMSampleBufferRef ) sampleBuffer
fromConnection: ( AVCaptureConnection * ) connection
I made a simple one-line modification to the imageFromSampleBuffer to correct the orientation problem (see my comment in the code under "I modified ..."). Hope it helps someone because I spent too much time on this.
// Create a UIImage from sample buffer data
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer {
// Get a CMSampleBuffer's Core Video image buffer for the media data
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// Lock the base address of the pixel buffer
CVPixelBufferLockBaseAddress(imageBuffer, 0);
// Get the number of bytes per row for the pixel buffer
void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
// Get the number of bytes per row for the pixel buffer
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
// Get the pixel buffer width and height
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
// Create a device-dependent RGB color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// Create a bitmap graphics context with the sample buffer data
CGContextRef context1 = CGBitmapContextCreate(baseAddress, width, height, 8,
bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
// Create a Quartz image from the pixel data in the bitmap graphics context
CGImageRef quartzImage = CGBitmapContextCreateImage(context1);
// Unlock the pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
// Free up the context and color space
CGContextRelease(context1);
CGColorSpaceRelease(colorSpace);
// Create an image object from the Quartz image
//I modified this line: [UIImage imageWithCGImage:quartzImage]; to the following to correct the orientation:
UIImage *image = [UIImage imageWithCGImage:quartzImage scale:1.0 orientation:UIImageOrientationRight];
// Release the Quartz image
CGImageRelease(quartzImage);
return (image);
}
Here is a right sequence:
AVCaptureVideoDataOutput *videoCaptureOutput = [[AVCaptureVideoDataOutput alloc] init];
if([self.captureSession canAddOutput:self.videoCaptureOutput]){
[self.captureSession addOutput:self.videoCaptureOutput];
}else{
NSLog(#"cantAddOutput");
}
// set portrait orientation
AVCaptureConnection *conn = [self.videoCaptureOutput connectionWithMediaType:AVMediaTypeVideo];
[conn setVideoOrientation:AVCaptureVideoOrientationPortrait];
For instance:
AVCaptureConnection *captureConnection = <a capture connection>;
if ([captureConnection isVideoOrientationSupported]) {
captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
}
The default appears to be AVCaptureVideoOrientationLandscapeRight.
See also QA1744: Setting the orientation of video with AV Foundation.
For those people that need to work with CIImage and orientation from buffer is wrong I used this correction.
As easy as that. BTW the numbers 3,1,6,8 are from here https://developer.apple.com/reference/imageio/kcgimagepropertyorientation
And don't ask me why 3,1,6,8 is the right combination. I used brute-force method to find it. If you know why let the explanation in a comment please...
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
// common way to get CIImage
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate);
CIImage *ciImage = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer
options:(__bridge NSDictionary *)attachments];
if (attachments) {
CFRelease(attachments);
}
// fixing the orientation of the CIImage
UIInterfaceOrientation curOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (curOrientation == UIInterfaceOrientationLandscapeLeft){
ciImage = [ciImage imageByApplyingOrientation:3];
} else if (curOrientation == UIInterfaceOrientationLandscapeRight){
ciImage = [ciImage imageByApplyingOrientation:1];
} else if (curOrientation == UIInterfaceOrientationPortrait){
ciImage = [ciImage imageByApplyingOrientation:6];
} else if (curOrientation == UIInterfaceOrientationPortraitUpsideDown){
ciImage = [ciImage imageByApplyingOrientation:8];
}
// ....
}
If the AVCaptureVideoPreviewLayer orientation is correct, you can simply set the orientation before you capture the image.
AVCaptureStillImageOutput *stillImageOutput;
AVCaptureVideoPreviewLayer *previewLayer;
NSData *capturedImageData;
AVCaptureConnection *videoConnection = [stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
if ([videoConnection isVideoOrientationSupported]) {
[videoConnection setVideoOrientation:previewLayer.connection.videoOrientation];
}
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
CFDictionaryRef exifAttachments =
CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
if (exifAttachments) {
// Do something with the attachments.
}
// TODO need to manually add GPS data to the image captured
capturedImageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
UIImage *image = [UIImage imageWithData:capturedImageData];
}];
Also, it's important to note that UIImageOrientation and AVCaptureVideoOrientation are different. UIImageOrientationUp refers to landscape mode with the volume controls down toward the ground (not up if you think about using the volume controls as a shutter button).
Thus, portrait orientation with the power button pointing to the sky (AVCaptureVideoOrientationPortrait) is actually UIImageOrientationLeft.
First of all, in the configuration of your video output, put these lines:
guard let connection = videoOutput.connection(withMediaType:
AVFoundation.AVMediaTypeVideo) else { return }
guard connection.isVideoOrientationSupported else { return }
guard connection.isVideoMirroringSupported else { return }
connection.videoOrientation = .portrait
connection.isVideoMirrored = position == .front
Then, configure your Target to support just Portait, by unchecking Landscape modes in General configuration.
(Source)
Orientation issue is with the front camera, so check device type and generate new image, it will definitely solve the orientation issue:
-(void)capture:(void(^)(UIImage *))handler{
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in self.stillImageOutput.connections)
{
for (AVCaptureInputPort *port in [connection inputPorts])
{
if ([[port mediaType] isEqual:AVMediaTypeVideo] )
{
videoConnection = connection;
break;
}
}
if (videoConnection) { break; }
}
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
if (imageSampleBuffer != NULL) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
**UIImage *capturedImage = [UIImage imageWithData:imageData];
if (self.captureDevice == [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo][1]) {
capturedImage = [[UIImage alloc] initWithCGImage:capturedImage.CGImage scale:1.0f orientation:UIImageOrientationLeftMirrored];
}**
handler(capturedImage);
}
}];
}
// #1
AVCaptureVideoOrientation newOrientation = AVCaptureVideoOrientationLandscapeRight;
if (#available(iOS 13.0, *)) {
// #2
for (AVCaptureConnection *connection in [captureSession connections]) {
if ([connection isVideoOrientationSupported]) {
connection.videoOrientation = newOrientation;
break;
}
} // #3
} else if ([previewLayer.connection isVideoOrientationSupported]) {
previewLayer.connection.videoOrientation = newOrientation;
}
Once that you can correctly use your AVCaptureSession, you can set a video orientation.
Here a detailed description of the code above. Remember, this code has to be executed after the [captureSession startRunning] execution:
Choose the orientation that you prefer
For ios version >= 13.0 you have to retrieve the active connection from the captureSession. Remember: only video connection supports videoOrientation
For ios version < 13.0 you can use the connection from the previewLayer
If your viewController doesn't have a fixed orientation you can set a new videoOrientation to your connection once the device orientation changes.
You can try this:
private func startLiveVideo() {
let captureSession = AVCaptureSession()
captureSession.sessionPreset = .photo
let captureDevice = AVCaptureDevice.default(for: .video)
let input = try! AVCaptureDeviceInput(device: captureDevice!)
let output = AVCaptureVideoDataOutput()
captureSession.addInput(input)
captureSession.addOutput(output)
output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
output.connection(with: .video)?.videoOrientation = .portrait
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.bounds
view.layer.addSublayer(previewLayer)
captureSession.startRunning()
}