質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

87.61%

swiftにてグレー化し、さらに数値化する

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,176

score 10

前提・実現したいこと

swift・プログラミング初心者です。

該当のソースコードは連写機能のカメラアプリのコードです。
そのソースコード内の↓↑の間にUIimageをグレー化し、
一枚ずつ数値を出せるようなコード入れたいのですが…
(関数を追加、UIImageを呼び出して計算…etc)

『試したこと』にあるコードではグレー化、数値化はともにできています。
そこでそれらのコードを使い、グレー化、数値化機能を導入しようと考えています。
ですがそのコードをどのように書き換え、追加すればいいのかわからない状況です。

※↓↑内は『試したこと』欄にあるコードなどを使えないかと思い書いただけです。

どうかご教示お願いいたします。

発生している問題・エラーメッセージ

エラーメッセージ

該当のソースコード

class ViewController: UIViewController ,AVCaptureVideoDataOutputSampleBufferDelegate,AVAudioPlayerDelegate  {

    @IBOutlet weak var previewView: UIImageView!
    @IBOutlet weak var snapButton: UIButton!

    @IBOutlet weak var scrollView: UIScrollView!

    var videoDataOutput: AVCaptureVideoDataOutput?
    var images:[UIImage] = []
    var isShooting = false // 連写中
    var counter = 0

    var audioPlayer:AVAudioPlayer! // シャッター音用

    override func viewDidLoad() {
        super.viewDidLoad()

        // シャッター音
        let audioPath = Bundle.main.path(forResource: "nc66359", ofType:"wav")!  //再生する音源のURL
        let audioUrl = URL(fileURLWithPath: audioPath)
        audioPlayer = try! AVAudioPlayer(contentsOf: audioUrl)
        audioPlayer.delegate = self          //デリゲートセット
        audioPlayer.currentTime = 0

        // セッションのインスタンス生成
        let captureSession = AVCaptureSession()

        //設定

        // 入力(背面カメラ)
        let videoDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
        videoDevice?.activeVideoMinFrameDuration = CMTimeMake(1, 30)// フレームレート 1/30秒
        let videoInput = try! AVCaptureDeviceInput.init(device: videoDevice)
        captureSession.addInput(videoInput)
        // 出力(ビデオデータ)
        videoDataOutput = AVCaptureVideoDataOutput()

        // ピクセルフォーマット(32bit BGRA)
        videoDataOutput?.videoSettings = [kCVPixelBufferPixelFormatTypeKey as AnyHashable : Int(kCVPixelFormatType_32BGRA)]//色情報
        // キューのブロック中に新しいフレームが来たら削除する
        videoDataOutput?.alwaysDiscardsLateVideoFrames = true
        // フレームをキャプチャするためのキューを指定
        videoDataOutput?.setSampleBufferDelegate(self, queue: DispatchQueue.main)

        captureSession.addOutput(videoDataOutput)
        // クオリティ(1920x1080ピクセル)
        captureSession.sessionPreset = AVCaptureSessionPreset1920x1080//画像の質

        // プレビュー
        if let videoLayer = AVCaptureVideoPreviewLayer.init(session: captureSession) {
            videoLayer.frame = previewView.bounds
            videoLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
            previewView.layer.addSublayer(videoLayer)
        }
        // セッションの開始
        DispatchQueue.global(qos: .userInitiated).async {
            captureSession.startRunning()
        }
    }

    // 新しいキャプチャの追加で呼ばれる(1/30秒に1回)
    func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
        if counter % 3 == 0 { // 1/10秒だけ処理する
            if isShooting {
                //        let image:UIImage = captureImage(sampleBuffer)
                //let image = UIImage(named: "003.jpg")

                let image = imageFromSampleBuffer(sampleBuffer: sampleBuffer)
                addImage(image: image)
            }
        }
        counter += 1
    }


 ↓↓↓   


    func ImageData(){
        let image = Image<RGBA<UInt8>>(uiImage: imageView.image!)

        var x,y:Int
        x = 0
        y = 0


        var sum:Int = 0
        for x in 0..<photowidth {
            for y in 0..<photoheight {
              sum = sum + (image[x,y].gray as AnyObject as! Int)
     }
    }

    var average = sum / photosize
    print("ave:", average)


    }




    ↑↑↑



    @IBAction func touchUpButton(_ sender: UIButton) {
        isShooting = false // 連写終了
        audioPlayer.stop()
    }

    @IBAction func touchDownButton(_ sender: UIButton) {
        // 前回撮影の画像を削除
        images = []
        for imageView in scrollView.subviews {
            imageView.removeFromSuperview()
        }
        isShooting = true // 連写中
        audioPlayer.play()
    }

    // 連写音の連続再生
    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        if isShooting {
            audioPlayer.play()
        }
    }


    //CMSampleBufferをUIImageに変換する
    func imageFromSampleBuffer(sampleBuffer :CMSampleBuffer) -> UIImage {
        let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)! // サンプルバッファからimageバッファを取り出す

        // イメージバッファのロック
        CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0))

        // 画像情報を取得
        let base = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0)!
        let bytesPerRow = UInt(CVPixelBufferGetBytesPerRow(imageBuffer))
        let width = UInt(CVPixelBufferGetWidth(imageBuffer))
        let height = UInt(CVPixelBufferGetHeight(imageBuffer))

        // ビットマップコンテキスト作成
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitsPerCompornent = 8
        let bitmapInfo = CGBitmapInfo(rawValue: (CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) as UInt32)
        let newContext = CGContext(data: base, width: Int(width), height: Int(height), bitsPerComponent: Int(bitsPerCompornent), bytesPerRow: Int(bytesPerRow), space: colorSpace, bitmapInfo: bitmapInfo.rawValue)! as CGContext

        // 画像作成
        let imageRef = newContext.makeImage()!
        let image = UIImage(cgImage: imageRef, scale: 1.0, orientation: UIImageOrientation.right)

        // イメージバッファのアンロック
        CVPixelBufferUnlockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0))
        return image
    }

    func addImage(image: UIImage) {

        let height:CGFloat = 100 // 基準の高さ
        let margin = 10
        let magnification = height / image.size.height

        let width = image.size.width * magnification
        let x = (margin + Int(width)) * images.count
        let y = margin

        let imageView = UIImageView(image: image)
        imageView.frame = CGRect(x: x, y: y, width: Int(width), height: Int(height))

        scrollView.addSubview(imageView)
        let w = x + Int(width)
        scrollView.contentSize = CGSize(width: CGFloat(w), height: scrollView.frame.size.height)
        images.append(image)



    }

}

試したこと

import UIKit
import EasyImagy

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

// 画面に出る子供の画像はプロジェクトに組み込み済み。 Main.storyboard で指定している

initImageView()
}

func initImageView(){
print("(in initImageView) ")
// 画像データファイルの読み込み:Main.storyboard に指定したものと同一
let imageUI1:UIImage = UIImage(named:"Baby1x.jpg")! // サイズは 593x896
let imageView = UIImageView(image:imageUI1)

/*
// 画面の中心を求めて
let screenWidth:CGFloat = view.frame.size.width
let screenHeight:CGFloat = view.frame.size.height

imageView.center = CGPoint(x:screenWidth/2, y:screenHeight/2)
// 下の行を実行すれば Main.storyboard に指定しなくても表示される?
//self.view.addSubview(imageView)
*/

let photowidth = imageUI1.size.width as AnyObject as! Int
let photoheight = imageUI1.size.height as AnyObject as! Int
let photosize  = photowidth * photoheight
print(photosize)
print(photoheight)
print(photowidth)
// Image にデータを代入
let myimage = Image<RGBA<UInt8>>(uiImage: imageView.image!)

var x,y:Int
x = 0
y = 0
//myimage[x,y]のデータをRGBAで表示する
print( myimage[x,y] )
print( myimage[250,450] )
print( myimage[592,895] )
print( myimage[592,895].gray)
//print( myimage[895,592] ) // 配列からはみ出でてエラーになる

var sum:Int = 0
for x in 0..<photowidth {
for y in 0..<photoheight {
sum = sum + (myimage[x,y].gray as AnyObject as! Int)
}
}

var average = sum / photosize
print("ave:", average)

print("(end of initImageView)")
}

}

補足情報(FW/ツールのバージョンなど)

EasyImageというフレームワークを使わせていただいています

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • takabosoft

    2019/01/16 17:38

    どこでつまづいているのでしょうか?

    キャンセル

  • NIGG

    2019/01/16 18:46

    該当のソースコードの↓↑内に、試したこと欄にあるコード等を使って数値を計算できるようにしたいのですが、どのようにコーディングしていけばいいかなど初歩的なことですがわかりません。
    初歩的なことで申し訳ありませんが、コードに流れの説明をつけていただけると幸いです。

    キャンセル

回答 1

0

躓いているポイントがよく判らないので、ほとんど推測ですが、

とりあえず組み込んだであろうfunc ImageData(){はおそらく正しく関数化できていません。

入力を画像(UIImage型)とし、出力をグレーの平均値となるように正しく書き直してください。
名前ももう少し関数の役割が判るような名前を付けてください。

例えば

/// 画像のグレースケール値の平均を求めます。
func calcGrayAverage(image: UIImage) -> Int {
    // TODO: ここは頑張って書いてください。
}

という関数を完成させてください。

開発の手順としては「試したこと」の方のプロジェクトを改造して
initImageView関数にかかれている一部をcalcGrayAverageとして抽出&関数化し、再利用できる形に加工してみると良いでしょう。

つまり、「試したこと」のコードの
print("ave:", average) 
という部分を
print("ave:", calcGrayAverage(image: imageView.image!)) 
と関数呼び出しに変えて同じ値が出るようなcalcGrayAverage関数を作るという意味です。

それが上手く行ったら、「該当のソースコード」の方へ関数をコピペし、呼び出して使うことができるはずです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

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

関連した質問

同じタグがついた質問を見る