Capture only camerapreview in AVCapture Swift - swift

here is my code
import UIKit
import AVFoundation
class ViewController: UIViewController {
#IBOutlet weak var cameraView: UIView!
var image: UIImage!
var captureSession = AVCaptureSession()
var backCamera: AVCaptureDevice?
var frontCamera: AVCaptureDevice?
var currentCamera: AVCaptureDevice?
var photoOutput: AVCapturePhotoOutput?
var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(_ animated: Bool) {
setupCaptureSession()
setupDevice()
setupInputOutput()
setupPreviewLayer()
startRunningCaptureSession()
}
#IBAction func cameraButton_Tab(_ sender: Any) {
let settings = AVCapturePhotoSettings()
// performSegue(withIdentifier: "showPhoto_Segue", sender: nil)
photoOutput?.capturePhoto(with: settings, delegate: self)
}
func setupCaptureSession() {
captureSession.sessionPreset = AVCaptureSession.Preset.photo
}
func setupDevice() {
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: AVCaptureDevice.Position.unspecified)
let devices = deviceDiscoverySession.devices
for device in devices {
if device.position == AVCaptureDevice.Position.back {
backCamera = device
}else if device.position == AVCaptureDevice.Position.front{
frontCamera = device
}
}
currentCamera = backCamera
}
func setupInputOutput() {
do{
let captureDeviceInput = try AVCaptureDeviceInput(device: currentCamera!)
captureSession.addInput(captureDeviceInput)
photoOutput = AVCapturePhotoOutput()
photoOutput?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey : AVVideoCodecType.jpeg])], completionHandler: nil)
captureSession.addOutput(photoOutput!)
}catch {
print(error)
}
}
func setupPreviewLayer() {
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
cameraPreviewLayer!.frame = self.cameraView.bounds
self.cameraView.layer.insertSublayer(cameraPreviewLayer!, at: 0)
}
func startRunningCaptureSession() {
captureSession.startRunning()
}
}
extension ViewController: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let imageData = photo.fileDataRepresentation(){
image = UIImage(data: imageData)
}
}
}
See the image,
I want save the image which background's color is yellow
I can see the camera through of that
But I save the image, it seems that save the whole view, not square.
I make the UIImageView same size of yellow UIView and save the output,
it takes the whole view capture and resize of that.
Like change rectangle to square with squeeze
How I cant catch just yellow background size and save?

This didFinishProcessingPhoto will return the complete image like what camera is seeing. You won't the image directly which is shown in your PreviewLayer. So, in order to get the UIImage of shown PreviewLayer, you can resize the captured image.
Well, resize can also be done in two ways: One keeping aspect ratio and other by passing the exact size. I would recommend to go with aspect ratio because it will ensure that your image won't be squeeze or streched from any size, while passing wrong size won't able to fulfil you requirement.
Resize UIImage passing new CGSize:
extension UIImage {
func scaleImage(toSize newSize: CGSize) -> UIImage? {
var newImage: UIImage?
let newRect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height).integral
UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
if let context = UIGraphicsGetCurrentContext(), let cgImage = self.cgImage {
context.interpolationQuality = .high
let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: newSize.height)
context.concatenate(flipVertical)
context.draw(cgImage, in: newRect)
if let img = context.makeImage() {
newImage = UIImage(cgImage: img)
}
UIGraphicsEndImageContext()
}
return newImage
}
}
Usage: capturedImage.scaleImage(toSize: CGSize(width: 300, height: 300))
Resize UIImage keeping aspect ratio:
extension UIImage {
func scaleImage(toWidth newWidth: CGFloat) -> UIImage {
let scale = newWidth / self.size.width
let newHeight = self.size.height * scale
let newSize = CGSize(width: newWidth, height: newHeight)
let renderer = UIGraphicsImageRenderer(size: newSize)
let image = renderer.image { (context) in
self.draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: newSize))
}
return image
}
}
Usage: capturedImage.scaleImage(toWidth: 300)
Reference: Resize UIImage to 200x200pt/px
Update:
Keep the below method as it is in your code:
#IBAction func cameraButton_Tab(_ sender: Any) {
let settings = AVCapturePhotoSettings()
photoOutput?.capturePhoto(with: settings, delegate: self)
}
extension ViewController: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let imageData = photo.fileDataRepresentation(){
let capturedImage = UIImage(data: imageData)
let cropImage = capturedImage.scaleImage(toWidth: cameraPreviewLayer!.frame.size.width) //It will return the Image size of Camera Preview
}
}
}

Related

How to show image from gallery in RealityKit?

I want to show image from gallery.
i am loading the image using imagePicker.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.originalImage] as? UIImage else { return }
if let imgUrl = info[UIImagePickerController.InfoKey.imageURL] as? URL{
self.photoEntity = createPhotoEntity(fileUrl: imgUrl, fileName: imgName)
}
dismiss(animated: true)
}
and then i have created a modelEntity like this.
private func createPhotoEntity(fileUrl: URL, fileName: String) -> ModelEntity? {
// Create a TextureResource by loading the contents of the file URL.
do {
let texture = try TextureResource.load(contentsOf: fileUrl)
let planeMesh = MeshResource.generatePlane(width: 0.5, depth: 0.5)
var material = UnlitMaterial()
material.color = .init(tint: .red.withAlphaComponent(0.999), texture: .init(texture))
let entity = ModelEntity(mesh: planeMesh, materials: [material])
entity.generateCollisionShapes(recursive: true)
ARView.installGestures([.scale, .translation], for: entity)
return entity
} catch(let error) {
print("error loading. \(error.localizedDescription)")
}
return nil
}
But the fact is, image is being shown with a color and this is legit.
But
Is there any way to show only image without any color?
Try this. Take into consideration, a tint color is multiplied by an image – so, if tint's RGBA = [1,1,1,1], a result of multiplication will be an image itself (without tinting)...
import ARKit
import RealityKit
class ViewController: UIViewController {
#IBOutlet var arView: ARView!
var anchor: AnchorEntity!
override func viewDidLoad() {
super.viewDidLoad()
self.anchor = AnchorEntity(world: [0,0,-1])
let ball: MeshResource = .generateSphere(radius: 0.25)
var material = UnlitMaterial()
if #available(iOS 15.0, *) {
material.color = try! .init(tint: .white,
texture: .init(.load(named: "img",
in: nil)))
}
let ballEntity = ModelEntity(mesh: ball, materials: [material])
self.anchor.addChild(ballEntity)
self.arView.scene.anchors.append(self.anchor)
}
}
Tested on Xcode 13.4, macOS 12.4
import Foundation
import RealityKit
import UIKit
import SwiftUI
#available(macOS 11.0, iOS 15.0, *)
public extension UnlitMaterial {
static func imageWith(imageName: String, bundle: Bundle? = nil, uiTint: UIColor) throws -> UnlitMaterial {
var material = UnlitMaterial()
do {
let texture = try TextureResource.load(named: imageName, in: bundle)
let materialParameter = MaterialParameters.Texture(texture)
let materialBaseColor = UnlitMaterial.BaseColor(tint: uiTint, texture: materialParameter)
material.color = materialBaseColor
return material
} catch let error {
throw error
}
}
static func imageWith(imageName: String, bundle: Bundle? = nil, tint: Color) throws -> UnlitMaterial {
var material = UnlitMaterial()
do {
let texture = try TextureResource.load(named: imageName, in: bundle)
let materialParameter = MaterialParameters.Texture(texture)
let materialBaseColor = UnlitMaterial.BaseColor(tint: UIColor(tint), texture: materialParameter)
material.color = materialBaseColor
return material
} catch let error {
throw error
}
}
}
Usage:
if #available(iOS 15.0, *) {
do {
var imageMaterial = try UnlitMaterial.imageWith(imageName: "1", uiTint: .white)
let bluePlane = ModelEntity(mesh: bluePlaneMeshResource, materials: [imageMaterial])
bluePlane.transform.rotation *= simd_quatf(angle: 90, axis: SIMD3<Float>(1,0,0))
box1AnchorEntity.addChild(bluePlane)
view.scene.addAnchor(box1AnchorEntity)
} catch let error {
print("Error: \(error)")
}
} else {
// Fallback on earlier versions
}

AVCapturePhotoOutput capturePhotoWithSettings:delegate: No active and enabled video connection'

Hi I am fairly new to coding and I ran into this error with AVCapturePhotoOutput. I am using Swift 5 and Xcode 12.5.1. I'm running it on my actual device and I have been stuck on it for awhile. The error only occurs when I try to capture a photo. Any help would be greatly appreciated thank you :)
private let output = AVCapturePhotoOutput()
private var captureSession: AVCaptureSession?
private let previewLayer = AVCaptureVideoPreviewLayer()
private let cameraView = UIView()
t
private func setUpCamera() {
let captureSession = AVCaptureSession()
if let device = AVCaptureDevice.default(for: .video) {
do {
let input = try AVCaptureDeviceInput(device: device)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
}
if captureSession.canAddOutput(output) {
captureSession.addOutput(output)
}
// Layer
previewLayer.session = captureSession
previewLayer.videoGravity = .resizeAspectFill
cameraView.layer.addSublayer(previewLayer)
captureSession.startRunning()
captureSession.sessionPreset = AVCaptureSession.Preset.high
self.captureSession = captureSession
}
catch {
print(error)
}
}
}
t
Extension CameraViewController: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
guard let data = photo.fileDataRepresentation(),
let image = UIImage(data: data) else {
return
}
captureSession?.stopRunning()
showEditPhoto(image: image)
}
private func showEditPhoto(image: UIImage) {
guard let resizedImage = image.sd_resizedImage(
with: CGSize(width: 640, height: 640),
scaleMode: .aspectFill
) else {
return
}
let vc = PostEditViewController(image: resizedImage)
if #available(iOS 14.0, *) {
vc.navigationItem.backButtonDisplayMode = .minimal
}
navigationController?.pushViewController(vc, animated: false)
}
Try using ‘ cgImageRepresentation()’ instead of ‘ fileDataRepresentation()’. And init a UIImage from the given cgImage

Swift - Overlay is not showing over AVCaptureVideoPreviewLayer

I'm trying to put an overlay on the preview layer but for some reason the UIImageView that I add to the preview layer is not shown.
Code:
let session = AVCaptureSession()
let sessionQueue = DispatchQueue(label: AVCaptureSession.self.description(), attributes: [], target: nil)
let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: 240, height: 80))
override func viewDidLoad() {
super.viewDidLoad()
imgView.image = UIImage(contentsOfFile: "angleArm.png")
session.beginConfiguration()
let videoDevice = AVCaptureDevice.default(.builtInDualCamera, for: AVMediaType.video, position: .back)
if (videoDevice != nil) {
let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice!)
if (videoDeviceInput != nil) {
if (session.canAddInput(videoDeviceInput!)) {
session.addInput(videoDeviceInput!)
}
}
}
session.commitConfiguration()
let previewLayer = AVCaptureVideoPreviewLayer(session: session)
previewLayer.frame = view.frame
previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.view.layer.addSublayer(previewLayer)
let preView = UIView()
preView.frame = self.view.frame
preView.addSubview(imgView)
self.view.addSubview(preView)
self.view.bringSubviewToFront(preView)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
sessionQueue.async {
self.session.startRunning()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sessionQueue.async {
self.session.stopRunning()
}
}
I've read some posts and tried to use CALayer and add the imageView to its contents and as of now I tried creating another UIView, put the imageView in it and add it to the main view, but still no luck.
Does anyone have an idea what I'm doing wrong? Thanks in advance.
You should check and change zPosition of overlay view. I think you should put overlayView.layer.zPosition > preview.layer.zPosition.
For example code:
overlayView.layer.zPosition = preview.layer.zPosition + 1
You try set previewLayer.zPosition = -1.

How to save resized images?

Good evening
I am trying to save the resized images in Documents Directory, to avoid having to resize every time it is called.
Analyzing the problem is just when I use the following line:
let jpgImageData = UIImageJPEGRepresentation (image, 1.0)
It appears that UIImageJPEGRepresentation undoes the changes made to the image and thus saves the image in the same ratio.
Anyone know a way to fix this?
Below the code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var imgView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let myImageName = "image_10001.jpg"
let imagePath = fileInDocumentsDirectory(filename: myImageName)
if let image = UIImage(named:"image_10001")?.resizedImage(newSize: UIScreen.main.bounds.size){
print("Image : \(image.size.height)")
_ = saveImage(image: image, path: imagePath)
}
if let loadedImage = loadImageFromPath(path: imagePath){
print("Screen: \(UIScreen.main.bounds.size.height)")
print("Image Size: \(loadedImage.size.height)")
// let image = UIImageJPEGRepresentation(loadedImage, 0.9)
// let imgRes = UIImage(data: image!)?.resizedImage(newSize: UIScreen.main.bounds.size)
////
// let load = loadedImage.resizedImage(newSize: UIScreen.main.bounds.size)
//
// print("Load: \(load.size.height)")
//
// _ = saveImage(image: load, path: imagePath)
//
imgView.image = loadedImage
//
// imgView.image = UIImage(data: image!)?.resizedImage(newSize: UIScreen.main.bounds.size)
}
// Do any additional setup after loading the view, typically from a nib.
}
func loadImageFromPath(path: String) -> UIImage? {
let image = UIImage(contentsOfFile: path)
if image == nil {
print("missing image at: \(path)")
}
print("Loading image from path: \(path)") // this is just for you to see the path in case you want to go to the directory, using Finder.
return image
}
func getDocumentsURL() -> URL {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
return documentsURL
}
func fileInDocumentsDirectory(filename: String) -> String {
let fileURL = getDocumentsURL().appendingPathComponent(filename)
return fileURL.path
}
func saveImage (image: UIImage, path: String ) -> Bool{
print("Save")
let url = URL(fileURLWithPath: path)
// let pngImageData = UIImagePNGRepresentation(image)
let jpgImageData = UIImageJPEGRepresentation(image, 1.0) // if you want to save as JPEG
let img = UIImage(data:jpgImageData!)
//
print("SIMG: \(String(describing: img?.size.height))")
do {
_ = try jpgImageData?.write(to: url, options: .atomicWrite)
print("Save Success")
} catch let error {
print(error)
return false
}
return true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension UIImage {
/// Returns a image that fills in newSize
func resizedImage(newSize: CGSize) -> UIImage {
// Guard newSize is different
guard self.size != newSize else { return self }
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0);
self.draw(in: CGRect(x:0, y:0, width:newSize.width, height: newSize.height))
let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
/// Returns a resized image that fits in rectSize, keeping it's aspect ratio
/// Note that the new image size is not rectSize, but within it.
func resizedImageWithinRect(rectSize: CGSize) -> UIImage {
let widthFactor = size.width / rectSize.width
let heightFactor = size.height / rectSize.height
var resizeFactor = widthFactor
if size.height > size.width {
resizeFactor = heightFactor
}
let newSize = CGSize(width:size.width/resizeFactor, height:size.height/resizeFactor)
let resized = resizedImage(newSize: newSize)
return resized
}
var jpeg: Data? {
return UIImageJPEGRepresentation(self, 1) // QUALITY min = 0 / max = 1
}
var png: Data? {
return UIImagePNGRepresentation(self)
}
}
extension Data {
var uiImage: UIImage? {
return UIImage(data: self)
}
}
I assume you have some "old" image inside your documents directory that was saved with some older code you had (not resized).
I assume that because the code you posted will not save any new images, because of this line:
if let image = UIImage(named:"image_10001")?.resizedImage(newSize: UIScreen.main.bounds.size){
//resize code
}
I think you wanted to use here same image name just taken from bundle:
if let image = UIImage(named: myImageName)?.resizedImage(newSize: UIScreen.main.bounds.size){
//resize code
}
You can Reset Simulator Content and confirm that no image will be loaded with the code you posted here.

How to custom size AVcapture

I need to capture for my barcode But this my code capture is full screen.
How to custom size or fix to small size.
please let my idea or code for custom size this thank you.
This my code capture is full screen.
import UIKit
import AVFoundation
protocol BarcodeDelegate {
func barcodeReaded(barcode: String)
}
class barcodeCapViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
var delegate: BarcodeDelegate?
var captureSession: AVCaptureSession!
var code: String?
override func viewDidLoad() {
super.viewDidLoad()
self.captureSession = AVCaptureSession();
let videoCaptureDevice: AVCaptureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
do {
let videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
if self.captureSession.canAddInput(videoInput) {
self.captureSession.addInput(videoInput)
} else {
print("Could not add video input")
}
let metadataOutput = AVCaptureMetadataOutput()
if self.captureSession.canAddOutput(metadataOutput) {
self.captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]
} else {
print("Could not add metadata output")
}
let previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
previewLayer.frame = self.view.layer.bounds
self.view.layer .addSublayer(previewLayer)
self.captureSession.startRunning()
} catch let error as NSError {
print("Error while creating vide input device: \(error.localizedDescription)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
for metadata in metadataObjects {
let readableObject = metadata as! AVMetadataMachineReadableCodeObject
let code = readableObject.stringValue
if !code.isEmpty {
self.captureSession.stopRunning()
self.dismissViewControllerAnimated(true, completion: nil)
self.delegate?.barcodeReaded(code)
}
}
}
}
When I add CGRectMake(20, 40, 200, 50)
show this
Add CGRectMake(20, 40, 500, 100)
show this
I don'n know why width and height not add up follow code.
Change the frame size of your AVCaptureVideoPreviewLayer:
let previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
previewLayer.frame = CGRectMake(10, 20, 100, 50) // something else!
If you're using autolayout, you probably don't want to deal with CALayer frames so you should create a UIView subclass, add your AVCaptureVideoPreviewLayer to that and set the layer's frame in layoutSubviews:
override func layoutSubviews() {
super.layoutSubviews()
self.previewLayer.frame = self.frame
}