AVCAPTURE 图像方向
AVCAPTURE image orientation
我有一个允许用户拍照的视图控制器。我将 avcapture 边界设置为屏幕上视图的边界。
在这个视图之上我有一个集合视图。所以用户可以拍摄多张照片,然后将它们添加到上面的集合视图中。
我无法在上面的预览中显示正确的方向。
代码如下:
@IBOutlet weak var imagePreviews: UICollectionView!
@IBOutlet weak var imgPreview: UIView!
var session: AVCaptureSession?
var stillImageOutput: AVCaptureStillImageOutput?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var images: [UIImage] = [UIImage]()
var isLandscapeLeft : Bool = false
var isLandscapeRight : Bool = false
var isPortrait : Bool = false
var isPortraitUpsideDown: Bool = false
@IBAction func capture(_ sender: UIButton)
{
if let videoConnection = stillImageOutput?.connection(withMediaType: AVMediaTypeVideo)
{
stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (sampleBuffer, error) in
if sampleBuffer != nil {
if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer), let dataProvider = CGDataProvider(data: imageData as CFData), let cgImageRef = CGImage(jpegDataProviderSource: dataProvider, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
{
var image: UIImage
if (self.isLandscapeLeft || self.isLandscapeRight)
{
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: self.isLandscapeLeft ? UIImageOrientation.left : UIImageOrientation.right)
} else {
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: self.isPortrait ? UIImageOrientation.up : UIImageOrientation.down)
}
image = self.fixOrientation(img: image)
self.images.append(image)
DispatchQueue.main.async {
self.imagePreviews.reloadData()
}
}
}
})
}
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let connection = self.videoPreviewLayer?.connection {
let currentDevice: UIDevice = UIDevice.current
let orientation: UIDeviceOrientation = currentDevice.orientation
let previewLayerConnection : AVCaptureConnection = connection
if (previewLayerConnection.isVideoOrientationSupported) {
switch (orientation) {
case .portrait:
isLandscapeLeft = false
isLandscapeRight = false
isPortrait = true
isPortraitUpsideDown = false
updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)
break
case .landscapeRight:
isLandscapeLeft = false
isLandscapeRight = true
isPortrait = false
isPortraitUpsideDown = false
updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeRight)
break
case .landscapeLeft:
isLandscapeLeft = true
isLandscapeRight = false
isPortrait = false
isPortraitUpsideDown = false
updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeLeft)
break
case .portraitUpsideDown:
isLandscapeLeft = false
isLandscapeRight = false
isPortrait = false
isPortraitUpsideDown = true
updatePreviewLayer(layer: previewLayerConnection, orientation: .portraitUpsideDown)
break
default:
updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)
break
}
}
}
}
func fixOrientation(img: UIImage) -> UIImage {
if (img.imageOrientation == .up) {
return img
}
UIGraphicsBeginImageContextWithOptions(img.size, false, img.scale)
let rect = CGRect(x: 0, y: 0, width: img.size.width, height: img.size.height)
img.draw(in: rect)
let normalizedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return normalizedImage
}
从纵向设备创建图像时,您应该考虑 UIImage
已旋转 90 度,例如:
switch UIApplication.shared.statusBarOrientation {
case .portrait:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .right)
case .landscapeRight:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .up)
case .landscapeLeft:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .down)
case .portraitUpsideDown:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .left)
default:
fatalError("Unexpected orientation")
}
有关完整示例,请参阅:
import UIKit
import AVFoundation
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var videoPreviewView: UIView!
var videoPreviewLayer: AVCaptureVideoPreviewLayer!
var session: AVCaptureSession = AVCaptureSession()
var stillImageOutput: AVCaptureStillImageOutput = {
let output = AVCaptureStillImageOutput()
output.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
return output
}()
var images: [UIImage] = [UIImage]()
var orientation = UIApplication.shared.statusBarOrientation
override func viewDidLoad() {
super.viewDidLoad()
guard let device = AVCaptureDevice.default(for: .video) else {
print("Unable to create capture device")
return
}
let input: AVCaptureDeviceInput
do {
input = try AVCaptureDeviceInput(device: device)
} catch {
print("Unable to create input", error)
return
}
guard session.canAddInput(input) else {
print("Cannot add input")
return
}
session.addInput(input)
guard session.canAddOutput(stillImageOutput) else {
print("Cannot add output")
return
}
session.addOutput(stillImageOutput)
session.startRunning()
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
videoPreviewView.layer.addSublayer(videoPreviewLayer)
}
@IBAction func didTapCaptureButton(_ sender: Any) {
if let connection = stillImageOutput.connection(with: .video) {
stillImageOutput.captureStillImageAsynchronously(from: connection) { sampleBuffer, error in
guard let sampleBuffer = sampleBuffer, error == nil else {
print(error ?? "Unknown error")
return
}
guard
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer),
let dataProvider = CGDataProvider(data: imageData as CFData),
let cgImageRef = CGImage(jpegDataProviderSource: dataProvider, decode: nil, shouldInterpolate: true, intent: .defaultIntent) else {
print("unable to capture image")
return
}
var image: UIImage?
switch self.orientation {
case .portrait:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .right)
case .landscapeRight:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .up)
case .landscapeLeft:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .down)
case .portraitUpsideDown:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .left)
default:
fatalError("Unexpected orientation")
}
guard image != nil else {
print("unable to create UIImage")
return
}
DispatchQueue.main.async {
self.images.append(image!)
self.imageView.image = image
}
}
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let connection = self.videoPreviewLayer?.connection {
orientation = UIApplication.shared.statusBarOrientation
switch orientation {
case .portrait: updatePreviewOrientation(for: connection, to: .portrait)
case .landscapeRight: updatePreviewOrientation(for: connection, to: .landscapeRight)
case .landscapeLeft: updatePreviewOrientation(for: connection, to: .landscapeLeft)
case .portraitUpsideDown: updatePreviewOrientation(for: connection, to: .portraitUpsideDown)
default: updatePreviewOrientation(for: connection, to: .portrait)
}
}
}
private func updatePreviewOrientation(for connection: AVCaptureConnection, to orientation: AVCaptureVideoOrientation) {
if connection.isVideoOrientationSupported {
connection.videoOrientation = orientation
}
videoPreviewLayer.frame = videoPreviewView.bounds
}
}
您正在手动转换 fixOrientation
中的图像方向。使用适当的 orientation
从 CGImage
创建 UIImage
后,如果不需要,您不必再重新创建图像。
在这个视图之上我有一个集合视图。所以用户可以拍摄多张照片,然后将它们添加到上面的集合视图中。
我无法在上面的预览中显示正确的方向。
代码如下:
@IBOutlet weak var imagePreviews: UICollectionView!
@IBOutlet weak var imgPreview: UIView!
var session: AVCaptureSession?
var stillImageOutput: AVCaptureStillImageOutput?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var images: [UIImage] = [UIImage]()
var isLandscapeLeft : Bool = false
var isLandscapeRight : Bool = false
var isPortrait : Bool = false
var isPortraitUpsideDown: Bool = false
@IBAction func capture(_ sender: UIButton)
{
if let videoConnection = stillImageOutput?.connection(withMediaType: AVMediaTypeVideo)
{
stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (sampleBuffer, error) in
if sampleBuffer != nil {
if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer), let dataProvider = CGDataProvider(data: imageData as CFData), let cgImageRef = CGImage(jpegDataProviderSource: dataProvider, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
{
var image: UIImage
if (self.isLandscapeLeft || self.isLandscapeRight)
{
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: self.isLandscapeLeft ? UIImageOrientation.left : UIImageOrientation.right)
} else {
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: self.isPortrait ? UIImageOrientation.up : UIImageOrientation.down)
}
image = self.fixOrientation(img: image)
self.images.append(image)
DispatchQueue.main.async {
self.imagePreviews.reloadData()
}
}
}
})
}
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let connection = self.videoPreviewLayer?.connection {
let currentDevice: UIDevice = UIDevice.current
let orientation: UIDeviceOrientation = currentDevice.orientation
let previewLayerConnection : AVCaptureConnection = connection
if (previewLayerConnection.isVideoOrientationSupported) {
switch (orientation) {
case .portrait:
isLandscapeLeft = false
isLandscapeRight = false
isPortrait = true
isPortraitUpsideDown = false
updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)
break
case .landscapeRight:
isLandscapeLeft = false
isLandscapeRight = true
isPortrait = false
isPortraitUpsideDown = false
updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeRight)
break
case .landscapeLeft:
isLandscapeLeft = true
isLandscapeRight = false
isPortrait = false
isPortraitUpsideDown = false
updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeLeft)
break
case .portraitUpsideDown:
isLandscapeLeft = false
isLandscapeRight = false
isPortrait = false
isPortraitUpsideDown = true
updatePreviewLayer(layer: previewLayerConnection, orientation: .portraitUpsideDown)
break
default:
updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)
break
}
}
}
}
func fixOrientation(img: UIImage) -> UIImage {
if (img.imageOrientation == .up) {
return img
}
UIGraphicsBeginImageContextWithOptions(img.size, false, img.scale)
let rect = CGRect(x: 0, y: 0, width: img.size.width, height: img.size.height)
img.draw(in: rect)
let normalizedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return normalizedImage
}
从纵向设备创建图像时,您应该考虑 UIImage
已旋转 90 度,例如:
switch UIApplication.shared.statusBarOrientation {
case .portrait:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .right)
case .landscapeRight:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .up)
case .landscapeLeft:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .down)
case .portraitUpsideDown:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .left)
default:
fatalError("Unexpected orientation")
}
有关完整示例,请参阅:
import UIKit
import AVFoundation
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var videoPreviewView: UIView!
var videoPreviewLayer: AVCaptureVideoPreviewLayer!
var session: AVCaptureSession = AVCaptureSession()
var stillImageOutput: AVCaptureStillImageOutput = {
let output = AVCaptureStillImageOutput()
output.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
return output
}()
var images: [UIImage] = [UIImage]()
var orientation = UIApplication.shared.statusBarOrientation
override func viewDidLoad() {
super.viewDidLoad()
guard let device = AVCaptureDevice.default(for: .video) else {
print("Unable to create capture device")
return
}
let input: AVCaptureDeviceInput
do {
input = try AVCaptureDeviceInput(device: device)
} catch {
print("Unable to create input", error)
return
}
guard session.canAddInput(input) else {
print("Cannot add input")
return
}
session.addInput(input)
guard session.canAddOutput(stillImageOutput) else {
print("Cannot add output")
return
}
session.addOutput(stillImageOutput)
session.startRunning()
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
videoPreviewView.layer.addSublayer(videoPreviewLayer)
}
@IBAction func didTapCaptureButton(_ sender: Any) {
if let connection = stillImageOutput.connection(with: .video) {
stillImageOutput.captureStillImageAsynchronously(from: connection) { sampleBuffer, error in
guard let sampleBuffer = sampleBuffer, error == nil else {
print(error ?? "Unknown error")
return
}
guard
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer),
let dataProvider = CGDataProvider(data: imageData as CFData),
let cgImageRef = CGImage(jpegDataProviderSource: dataProvider, decode: nil, shouldInterpolate: true, intent: .defaultIntent) else {
print("unable to capture image")
return
}
var image: UIImage?
switch self.orientation {
case .portrait:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .right)
case .landscapeRight:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .up)
case .landscapeLeft:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .down)
case .portraitUpsideDown:
image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .left)
default:
fatalError("Unexpected orientation")
}
guard image != nil else {
print("unable to create UIImage")
return
}
DispatchQueue.main.async {
self.images.append(image!)
self.imageView.image = image
}
}
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let connection = self.videoPreviewLayer?.connection {
orientation = UIApplication.shared.statusBarOrientation
switch orientation {
case .portrait: updatePreviewOrientation(for: connection, to: .portrait)
case .landscapeRight: updatePreviewOrientation(for: connection, to: .landscapeRight)
case .landscapeLeft: updatePreviewOrientation(for: connection, to: .landscapeLeft)
case .portraitUpsideDown: updatePreviewOrientation(for: connection, to: .portraitUpsideDown)
default: updatePreviewOrientation(for: connection, to: .portrait)
}
}
}
private func updatePreviewOrientation(for connection: AVCaptureConnection, to orientation: AVCaptureVideoOrientation) {
if connection.isVideoOrientationSupported {
connection.videoOrientation = orientation
}
videoPreviewLayer.frame = videoPreviewView.bounds
}
}
您正在手动转换 fixOrientation
中的图像方向。使用适当的 orientation
从 CGImage
创建 UIImage
后,如果不需要,您不必再重新创建图像。