1

When I'm making a customized camera app in Swift. But when I try to access the camera, the view in camera rotated 90 degrees. I try to find the solution. One solution I find is adding a fixOrientation function to fix the view. But not working... Here is my full code:

let CIHueAdjust = "CIHueAdjust"
let CIHueAdjustFilter = CIFilter(name: "CIHueAdjust", withInputParameters: ["inputAngle" : 1.24])

let Filters = [CIHueAdjust: CIHueAdjustFilter]

let FilterNames = [String](Filters.keys).sort()

class LiveCamViewController : UIViewController,AVCaptureVideoDataOutputSampleBufferDelegate{
let mainGroup = UIStackView()
let imageView = UIImageView(frame: CGRectZero)
let filtersControl = UISegmentedControl(items: FilterNames)

override func viewDidLoad()
{
    super.viewDidLoad()

    view.addSubview(mainGroup)
    mainGroup.axis = UILayoutConstraintAxis.Vertical
    mainGroup.distribution = UIStackViewDistribution.Fill

    mainGroup.addArrangedSubview(imageView)
    mainGroup.addArrangedSubview(filtersControl)

    imageView.contentMode = UIViewContentMode.ScaleAspectFit

    filtersControl.selectedSegmentIndex = 0

    let captureSession = AVCaptureSession()
    captureSession.sessionPreset = AVCaptureSessionPresetPhoto

    let backCamera = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)

    do
    {
        let input = try AVCaptureDeviceInput(device: backCamera)

        captureSession.addInput(input)
    }
    catch
    {
        print("can't access camera")
        return
    }

    //get captureOutput invoked
    let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    view.layer.addSublayer(previewLayer)

    let videoOutput = AVCaptureVideoDataOutput()

    videoOutput.setSampleBufferDelegate(self, queue: dispatch_queue_create("sample buffer delegate", DISPATCH_QUEUE_SERIAL))
    if captureSession.canAddOutput(videoOutput)
    {
        captureSession.addOutput(videoOutput)
    }

    captureSession.startRunning()
}

func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!)
{
    guard let filter = Filters[FilterNames[filtersControl.selectedSegmentIndex]] else
    {
        return
    }

    let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
    let cameraImage = CIImage(CVPixelBuffer: pixelBuffer!)

    filter!.setValue(cameraImage, forKey: kCIInputImageKey)

    let filteredImage = UIImage(CIImage: filter!.valueForKey(kCIOutputImageKey) as! CIImage!)
    let fixedImage = fixOrientation(filteredImage)

    dispatch_async(dispatch_get_main_queue())
    {
        self.imageView.image = fixedImage
    }
}

func fixOrientation(image: UIImage) -> UIImage {
    if (image.imageOrientation == UIImageOrientation.Up) {
        return image;
    }

    print(image.imageOrientation)

    var transform = CGAffineTransformIdentity

    switch (image.imageOrientation) {
    case .Down, .DownMirrored:
        transform = CGAffineTransformTranslate(transform, image.size.width, image.size.height)
        transform = CGAffineTransformRotate(transform, CGFloat(M_PI))
        break
    case .Left, .LeftMirrored:
        transform = CGAffineTransformTranslate(transform, image.size.width, 0)
        transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2))
        break
    case .Right, .RightMirrored:
        transform = CGAffineTransformTranslate(transform, 0, image.size.height)
        transform = CGAffineTransformRotate(transform, CGFloat(-M_PI_2))
        break
    case .Up, .UpMirrored:
        break
    }

    switch (image.imageOrientation) {
    case .UpMirrored, .DownMirrored:
        transform = CGAffineTransformTranslate(transform, image.size.width, 0)
        transform = CGAffineTransformScale(transform, -1, 1)
        break
    case .LeftMirrored, .RightMirrored:
        transform = CGAffineTransformTranslate(transform, image.size.height, 0)
        transform = CGAffineTransformScale(transform, -1, 1)
        break
    case .Up, .Down, .Left, .Right:
        break
    }

    //Draw the underlying CGImage into a new context, applying the transform
    let ctx = CGBitmapContextCreate(nil, Int(image.size.width), Int(image.size.height), CGImageGetBitsPerComponent(image.CGImage), 0, CGImageGetColorSpace(image.CGImage), UInt32(CGImageGetBitmapInfo(image.CGImage).rawValue))

    CGContextConcatCTM(ctx, transform);

    switch (image.imageOrientation) {
    case .Left, .LeftMirrored, .Right, .RightMirrored:
        CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.height, image.size.width), image.CGImage)
        break
    default:
        CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage)
        break
    }

    let cgimg = CGBitmapContextCreateImage(ctx)
    let img = UIImage(CGImage:cgimg!)

    return img
}

override func viewDidLayoutSubviews()
{
    mainGroup.frame = CGRect(x: 37, y: 115, width: 301, height: 481)
}

}

I set a breakpoint to test, the code seems only run until

if (image.imageOrientation == UIImageOrientation.Up) {
        return image;
    }

then it returns the same view...

Can anyone help me? Thanks!!!

Hasya
  • 9,792
  • 4
  • 31
  • 46
dididaisy
  • 141
  • 3
  • 10
  • So image.imageOrientation == UIImageOrientation.Up is true , comment that part if you want to test logic below. – LoVo Apr 25 '16 at 08:48
  • @LoVo Hi, thx for replying. I have commented those. But I still have some errors. Like CGBitmapContextCreate: unsupported parameter combination, CGContextConcatCTM: invalid context 0x0, CGContextDrawImage: invalid context 0x0, CGBitmapContextCreateImage: invalid context 0x0. I have no idea what wrong :( – dididaisy Apr 25 '16 at 10:38
  • Could you check the values of `ctx` and `transform`before it is crashing ? – LoVo Apr 25 '16 at 10:58
  • @LoVo Sorry, how to do that? – dididaisy Apr 25 '16 at 11:11
  • Set a breakpoint before the line your app is crashing but after the values are set. – LoVo Apr 25 '16 at 11:12
  • @LoVo Hi ctx has the value: nil, and transform has the value: (a=1, b=0, c=0, d=1, tx=0, ty=0) – dididaisy Apr 25 '16 at 11:38
  • I am having a very similar problem. Were you ever able to resolve this issue? I cannot find a solution anywhere @ShidiYang – Ryan Tobin Jan 07 '17 at 05:14
  • @RyanTobin Yes I resolved. I changed if (image.imageOrientation == UIImageOrientation.Up) to UIImageOrientation.Right – dididaisy Mar 14 '17 at 07:01

3 Answers3

6

In capture() where you get your CIImage from CMSampleBuffer do this after:

   yourCIImage = yourCIImage.applyingOrientation(6)

Change the number from 1-8 depending on how you want to rotate and mirror. https://developer.apple.com/documentation/imageio/kcgimagepropertyorientation

Luke Stanyer
  • 1,404
  • 12
  • 21
1

For those who need the code from @LoVo in Swift 3:

func rotateCameraImageToProperOrientation(imageSource : UIImage, maxResolution : CGFloat) -> UIImage {

    guard let imgRef = imageSource.cgImage else {
        return imageSource
    }

    let width = CGFloat(imgRef.width);
    let height = CGFloat(imgRef.height);

    var bounds = CGRect(x: 0, y: 0, width: width, height: height)

    var scaleRatio : CGFloat = 1
    if (width > maxResolution || height > maxResolution) {

        scaleRatio = min(maxResolution / bounds.size.width, maxResolution / bounds.size.height)
        bounds.size.height = bounds.size.height * scaleRatio
        bounds.size.width = bounds.size.width * scaleRatio
    }

    var transform = CGAffineTransform.identity
    let orient = imageSource.imageOrientation
    let imageSize = CGSize(width: CGFloat(imgRef.width), height: CGFloat(imgRef.height))


    switch(imageSource.imageOrientation) {
    case .up :
        transform = CGAffineTransform.identity

    case .upMirrored :
        transform = CGAffineTransform(translationX: imageSize.width, y: 0.0)
        transform = transform.scaledBy(x: -1.0, y: 1.0)

    case .down :
        transform = CGAffineTransform(translationX: imageSize.width, y: imageSize.height)
        transform = transform.rotated(by: CGFloat(M_PI))

    case .downMirrored :
        transform = CGAffineTransform(translationX: 0.0, y: imageSize.height)
        transform = transform.scaledBy(x: 1.0, y: -1.0)

    case .left :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = storedHeight
        transform = CGAffineTransform(translationX: 0.0, y: imageSize.width)
        transform = transform.rotated(by: 3.0 * CGFloat(M_PI) / 2.0)

    case .leftMirrored :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = storedHeight
        transform = CGAffineTransform(translationX: imageSize.height, y: imageSize.width)
        transform = transform.scaledBy(x: -1.0, y: 1.0)
        transform = transform.rotated(by: 3.0 * CGFloat(M_PI) / 2.0)

    case .right :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = storedHeight
        transform = CGAffineTransform(translationX: imageSize.height, y: 0.0)
        transform = transform.rotated(by: CGFloat(M_PI) / 2.0)

    case .rightMirrored :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = storedHeight
        transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
        transform = transform.rotated(by: CGFloat(M_PI) / 2.0)
    }

    UIGraphicsBeginImageContext(bounds.size)
    guard let context = UIGraphicsGetCurrentContext() else {
        return imageSource
    }

    if orient == .right || orient == .left {
        context.scaleBy(x: -scaleRatio, y: scaleRatio)
        context.translateBy(x: -height, y: 0)
    } else {
        context.scaleBy(x: scaleRatio, y: -scaleRatio)
        context.translateBy(x: 0, y: -height)
    }

    context.concatenate(transform);
    context.draw(imgRef, in: CGRect(x: 0, y: 0, width: width, height: height))

    guard let imageCopy = UIGraphicsGetImageFromCurrentImageContext() else {
        return imageSource
    }
    UIGraphicsEndImageContext();

    return imageCopy;
}
0

Try this method instead: (taken from here)

import Darwin

class func rotateCameraImageToProperOrientation(imageSource : UIImage, maxResolution : CGFloat) -> UIImage {

let imgRef = imageSource.CGImage;

let width = CGFloat(CGImageGetWidth(imgRef));
let height = CGFloat(CGImageGetHeight(imgRef));

var bounds = CGRectMake(0, 0, width, height)

var scaleRatio : CGFloat = 1
if (width > maxResolution || height > maxResolution) {

    scaleRatio = min(maxResolution / bounds.size.width, maxResolution / bounds.size.height)
    bounds.size.height = bounds.size.height * scaleRatio
    bounds.size.width = bounds.size.width * scaleRatio
}

var transform = CGAffineTransformIdentity
let orient = imageSource.imageOrientation
let imageSize = CGSizeMake(CGFloat(CGImageGetWidth(imgRef)), CGFloat(CGImageGetHeight(imgRef)))


switch(imageSource.imageOrientation) {
case .Up :
    transform = CGAffineTransformIdentity

case .UpMirrored :
    transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
    transform = CGAffineTransformScale(transform, -1.0, 1.0);

case .Down :
    transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
    transform = CGAffineTransformRotate(transform, CGFloat(M_PI));

case .DownMirrored :
    transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
    transform = CGAffineTransformScale(transform, 1.0, -1.0);

case .Left :
    let storedHeight = bounds.size.height
    bounds.size.height = bounds.size.width;
    bounds.size.width = storedHeight;
    transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
    transform = CGAffineTransformRotate(transform, 3.0 * CGFloat(M_PI) / 2.0);

case .LeftMirrored :
    let storedHeight = bounds.size.height
    bounds.size.height = bounds.size.width;
    bounds.size.width = storedHeight;
    transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
    transform = CGAffineTransformScale(transform, -1.0, 1.0);
    transform = CGAffineTransformRotate(transform, 3.0 * CGFloat(M_PI) / 2.0);

case .Right :
    let storedHeight = bounds.size.height
    bounds.size.height = bounds.size.width;
    bounds.size.width = storedHeight;
    transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
    transform = CGAffineTransformRotate(transform, CGFloat(M_PI) / 2.0);

case .RightMirrored :
    let storedHeight = bounds.size.height
    bounds.size.height = bounds.size.width;
    bounds.size.width = storedHeight;
    transform = CGAffineTransformMakeScale(-1.0, 1.0);
    transform = CGAffineTransformRotate(transform, CGFloat(M_PI) / 2.0);

default : ()
}

UIGraphicsBeginImageContext(bounds.size)
let context = UIGraphicsGetCurrentContext()

if orient == .Right || orient == .Left {
    CGContextScaleCTM(context, -scaleRatio, scaleRatio);
    CGContextTranslateCTM(context, -height, 0);
} else {
    CGContextScaleCTM(context, scaleRatio, -scaleRatio);
    CGContextTranslateCTM(context, 0, -height);
}

CGContextConcatCTM(context, transform);
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);

let imageCopy = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return imageCopy;
}
Community
  • 1
  • 1
LoVo
  • 1,856
  • 19
  • 21
  • Sorry. It still not works for me. Before I put any fixOrientation method: The view is counterclockwise 90 degrees rotated, and it is viewing correctly when you rotate you phone counterclockwise 90 degrees (take the phone horizontally). After I add you method, the view is always keep changing counterclockwise 90 degrees whatever I rotate the direction of the phone – dididaisy Apr 25 '16 at 12:28
  • Sorry. My fault. Actually this method does not change anything – dididaisy Apr 25 '16 at 12:41
  • Sorry, When I execute this code, it still crushing with the errors:CGContextScaleCTM: invalid context 0x0. CGContextTranslateCTM: invalid context 0x0. CGContextConcatCTM: invalid context 0x0. CGContextDrawImage: invalid context 0x0. – dididaisy Apr 25 '16 at 13:01
  • yes, I have also tried commented this if statement. The view is still rotated. – dididaisy Apr 25 '16 at 13:03