カメラ画像に画像を重ねて保存するアプリについて

受付中

回答 0

投稿

  • 評価
  • クリップ 0
  • VIEW 626

shsw228

score 13

現在カメラ画像に対して透過PNGを重ねて保存する,ARカメラのようなものを書いています.
重ねる画像は今の所,iphoneXであればナビゲーションバーを貫通する最下部に接地,左右に触れるように拡大する配置,また撮影は縦画面のみ考えています.

勉強中でひとまず検索して見つけたコードをどうにか形にしたような段階です.的外れなことを言っていたら申し訳ありません.
  
  
  

質問事項は大まかに3つあります

  1. プレビュー表示に用いたViewを直接画像に書き出せないのは正しいか
  2. 重ねる画像のサイズ指定について
  3. Autolayoutのコードでの拘束について
      
      

コードのほぼすべてを下に示しております.手探りで無駄,足りないなどツッコミどころが多いと思われます.
  

自分がコードを読んでいて,
カメラの画像と載せる画像をプレビューで表示するためのViewと,それとは別に保存するために再描画しないといけないと思っていましたがこれは正しいでしょうか?
また,再描画の時点でUIImageViewの位置は使えないのでしょうか? 

frame_image.draw(in:CGRect(x: 0, y: 523, width: self.view.bounds.width, height:289))


暫定的に重ねる画像のyを523とベタ打ち(Autolayoutで配置した際の数値)していますがおそらく自分の環境でしかキレイに出ないことは想像できます.
下基準での画像高さは,画像のサイズ比を出す→画面横幅に掛けて縦を出す→代入が正しい手順でしょうか?

  
  
  
最後にAutolayoutについてですが,今回のようにUIImageViewが画面サイズとピッタリ揃う値を代入しているような場合にも拘束は必要なのでしょうか.

一応動いてしまっているのもあり何が良くない書き方なのかがわかりません.
まだ気にするような域でないかもとも思いますが,回答お待ちしております.
  
  
対象:ios11.0~
テスト機:iPhoneX iOS 12.0

import UIKit
import AVFoundation
import CoreImage


class ViewController: UIViewController,UIGestureRecognizerDelegate, AVCapturePhotoCaptureDelegate {
    @IBOutlet weak var  imageView:UIImageView!
    var input:AVCaptureDeviceInput!
    var output:AVCapturePhotoOutput!
    var session:AVCaptureSession!
    var preView:UIView!
    var camera:AVCaptureDevice!
    let frame_image:UIImage = UIImage(named: "Assetsに登録している素材")!
    private var photoData: Data?


    override func viewDidLoad() {
        super.viewDidLoad()

        // 画面タップでシャッターを切るための設定
        let tapGesture:UITapGestureRecognizer = UITapGestureRecognizer(
            target: self, action: #selector(ViewController.tapped(_:)))
        // デリゲートをセット
        tapGesture.delegate = self;
        // Viewに追加.
        self.view.addGestureRecognizer(tapGesture)

        imageView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant:0.0).isActive = true
        imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant:0.0).isActive = true
        imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant:0.0).isActive = true

    }

    override func viewWillAppear(_ animated: Bool) {
        // スクリーン設定
        setupDisplay()
        // カメラの設定
        setupCamera()
    }

    override func viewDidDisappear(_ animated: Bool) {
        // camera stop メモリ解放
        session.stopRunning()

        for output in session.outputs {
            //session.removeOutput((output as? AVCaptureOutput)!)
            session.removeOutput(output)
        }

        for input in session.inputs {
            //session.removeInput((input as? AVCaptureInput)!)
            session.removeInput(input)
        }
        session = nil
        camera = nil
    }

    func setupDisplay(){
        //スクリーンの幅
        let screenWidth = UIScreen.main.bounds.size.width;
        //スクリーンの高さ
        let screenHeight = UIScreen.main.bounds.size.height;
        //透過画像定義
        // プレビュー用のビューを生成
        preView = UIView(frame: CGRect(x: 0.0, y: 0.0,
                                       width: screenWidth, height: screenHeight))



    }

    // camera initialize
    func setupCamera(){
        // セッション
        session = AVCaptureSession()

        // 背面・前面カメラの選択
        camera = AVCaptureDevice.default(
            AVCaptureDevice.DeviceType.builtInWideAngleCamera,for: AVMediaType.video,position: .back)
        // position: .front

        // カメラからの入力データ
        do {
            input = try AVCaptureDeviceInput(device: camera)

        } catch let error as NSError {
            print(error)
        }

        // 入力をセッションに追加
        if(session.canAddInput(input)) {
            session.addInput(input)
        }

        // 静止画出力のインスタンス生成
        output = AVCapturePhotoOutput()

        // 出力をセッションに追加
        if(session.canAddOutput(output)) {
            session.addOutput(output)
        }

        // セッションからプレビューを表示を
        let previewLayer = AVCaptureVideoPreviewLayer(session: session)

        previewLayer.frame = preView.frame
        previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill

        // 画像によってimageviewのサイズを変更
        imageView.image = frame_image
        let scale = self.view.bounds.width/frame_image.size.width
        imageView.translatesAutoresizingMaskIntoConstraints = true
        imageView.frame = CGRect(
            x: imageView.frame.minX,
            y: imageView.frame.minY,
            width: self.view.bounds.width,
            height: scale * frame_image.size.height
        )


        // レイヤーをViewに設定

        self.view.layer.addSublayer(previewLayer)
        self.view.addSubview(imageView)
        session.startRunning()
    }


    // タップイベント.
    @objc func tapped(_ sender: UITapGestureRecognizer){
        print("タップ")
        takeStillPicture()
    }


    func takeStillPicture(){

        let photoSettings = AVCapturePhotoSettings()
        photoSettings.flashMode = .auto
        photoSettings.isAutoStillImageStabilizationEnabled = true
        photoSettings.isHighResolutionPhotoEnabled = false

        output?.capturePhoto(with: photoSettings, delegate: self)
    }

    func photoOutput(_ output: AVCapturePhotoOutput,didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {

        let imageData = photo.fileDataRepresentation()
        let PhotoData:UIImage? = UIImage(data: imageData!)

        UIGraphicsBeginImageContext(view.frame.size)

        PhotoData?.draw(in:CGRect(origin: CGPoint.zero, size: view.frame.size))
        frame_image.draw(in:CGRect(x: 0, y: 523, width: self.view.bounds.width, height:289))

        let compositedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()


        UIImageWriteToSavedPhotosAlbum(compositedImage!, self, nil, nil)
    }


}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる