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

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

新規登録して質問してみよう
ただいま回答率
85.50%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

Q&A

解決済

1回答

1314閲覧

[Swift3]カメラ画像→ピクセルデータ配列→UIImageと順番に変換したい[編集]

nobu09

総合スコア33

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

0グッド

0クリップ

投稿2018/07/31 04:24

編集2018/08/03 10:58

前提・実現したいこと

カメラからのキャプチャ画像に何かしらの操作をして、その画像をUIImageViewで表示したいです。

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

キャプチャ画像→ピクセルデータ配列→何かしらの処理→UIImageという処理をしたいのですが、
メソッド内で作成したUIImagを返り値で別の変数に代入した際、タイミングによって代入先の画像が壊れています。

具体的には、以下のソースコード内の
(1)の部分でreturnした場合は、正常に画像が渡され表示できます。
(2)の部分でreturnした場合は、異常な画像データが渡されてしまいます。

該当のソースコード[編集]

swift

1class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate { 2 3 @IBOutlet var previewView: UIImageView! 4 5 var session = AVCaptureSession() 6 var videoOutput = AVCaptureVideoDataOutput() 7 8 var images:[UIImage] = [] 9 10 var count: Int = 0 11 12 override func viewDidLoad() { 13 super.viewDidLoad() 14 15 if session.isRunning { 16 return 17 } 18 19 images.removeAll() 20 21 setupInputOutput() 22 23 self.session.startRunning() 24 } 25 26 override func didReceiveMemoryWarning() { 27 super.didReceiveMemoryWarning() 28 } 29 30 func setupInputOutput() { 31 32 session.sessionPreset = AVCaptureSession.Preset.medium 33 34 do { 35 let device = AVCaptureDevice.default( 36 AVCaptureDevice.DeviceType.builtInWideAngleCamera, 37 for: AVMediaType.video, 38 position: AVCaptureDevice.Position.back 39 ) 40 device?.activeVideoMinFrameDuration = CMTimeMake(1, 30) 41 42 let input = try! AVCaptureDeviceInput(device: device!) 43 if session.canAddInput(input) { 44 session.addInput(input) 45 } else { 46 print("Can't add input to session") 47 return 48 } 49 } catch let error as NSError { 50 print("no camera (error)") 51 return 52 } 53 54 videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as AnyHashable : Int(kCVPixelFormatType_32BGRA)] as! [String : Any] 55 videoOutput.alwaysDiscardsLateVideoFrames = true 56 videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) 57 if session.canAddOutput(videoOutput) { 58 session.addOutput(videoOutput) 59 } else { 60 print("Can't add output to session") 61 return 62 } 63 } 64 65 func setPreviewLayer() { 66 let previewLayer = AVCaptureVideoPreviewLayer(session: session) 67 guard let videoLayer: AVCaptureVideoPreviewLayer = previewLayer else { 68 print("Can't create preview layer.") 69 return 70 } 71 72 videoLayer.frame = previewView.bounds 73 videoLayer.masksToBounds = true 74 videoLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill 75 previewView.layer.addSublayer(videoLayer) 76 } 77 78 func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { 79 let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)! 80 // イメージバッファのロック 81 CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0)) 82 83 self.imageFromSampleBuffer(sampleBuffer: sampleBuffer) 84 85 CVPixelBufferUnlockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0)) 86 87 DispatchQueue.main.async { 88 self.previewView.image = image 89 } 90 } 91 92 func imageFromSampleBuffer(sampleBuffer :CMSampleBuffer) -> UIImage { 93 let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)! 94 95 // 画像情報を取得 96 let base = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0)! 97 let bytesPerRow = UInt(CVPixelBufferGetBytesPerRow(imageBuffer)) 98 let width = UInt(CVPixelBufferGetWidth(imageBuffer)) 99 let height = UInt(CVPixelBufferGetHeight(imageBuffer)) 100 101 // ビットマップコンテキスト作成 102 let colorSpace = CGColorSpaceCreateDeviceRGB() 103 let bitsPerCompornent = 8 104 let bitmapInfo = CGBitmapInfo(rawValue: (CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) as UInt32) 105 let newContext = CGContext(data: base, width: Int(width), height: Int(height), bitsPerComponent: Int(bitsPerCompornent), bytesPerRow: Int(bytesPerRow), space: colorSpace, bitmapInfo: bitmapInfo.rawValue)! as CGContext 106 107 // 画像作成 108 let imageRef = newContext.makeImage()! 109 110 // (1)ここでimgを返すとキャプチャした画像が正常に表示される 111 //let img = UIImage(cgImage: imageRef, scale: 1.0, orientation: UIImageOrientation.right) 112 //return img 113 114 let data = imageRef.dataProvider!.data 115 let length = CFDataGetLength(data) 116 var rawData = [UInt8](repeating: 0, count: length) 117 CFDataGetBytes(data, CFRange(location: 0, length: length), &rawData) 118 119 let provider = CGDataProvider(dataInfo: nil,data: &rawData, size: length,releaseData: releaseData) 120 let bitsPerPixel = imageRef.bitsPerPixel 121 let cgImage = CGImage(width: Int(width), height: Int(height), bitsPerComponent: bitsPerCompornent, bitsPerPixel: bitsPerPixel, bytesPerRow:Int(bytesPerRow), space: colorSpace, bitmapInfo: bitmapInfo, provider: provider!, decode: nil, shouldInterpolate: false, intent: .defaultIntent) 122 123 let editedimage = UIImage(cgImage:cgImage!, scale:1.0, orientation:UIImageOrientation.right) 124 125 // (2)ここで返すと画像が異常になる。 126 return editedimage 127 } 128} 129

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

xcode Version 9.4.1
Swift4.0

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fuzzball

2018/08/03 00:07

タイミングによってということは、正しく表示されることもあるのでしょうか?(元画像の違いではなく、同じ画像で正しかったり壊れたりする?)
nobu09

2018/08/03 01:04 編集

正しく表示はされません。ただ、ソースコードの(2)のタイミングの画像が壊れかたが違います。真っ白であったり、ピクセル情報がずれているような画像になったりします。
fuzzball

2018/08/03 01:11

(2)の「 editedimageには画像データが正常に入っている」というのはどういう意味ですか?
fuzzball

2018/08/03 01:20

「真っ白であったり」というのは正しいということではないのでしょうか?
fuzzball

2018/08/03 01:21

for (index, _) in rawData.enumerated() {rawData[index] = 255} をコメントアウトすると、画像は壊れなくなりますか?やはり壊れますか?
nobu09

2018/08/03 01:30 編集

コメントアウトしてもしなくても壊れます。
fuzzball

2018/08/03 01:31

10:11と10:20への返信をお願いします。
nobu09

2018/08/03 01:39

10:11:ブレークポイントを設定し、中断後カーソルを変数に合わせquick viewで確認しています。10:20:コメントアウトしてもしなくても壊れます。
fuzzball

2018/08/03 01:41

「カーソルを変数に合わせquick viewで確認しています」→ちょっと確認できないのですが、デバッガ上で画像が表示されるということでしょうか?
fuzzball

2018/08/03 01:42

10:20の質問は、「正しく表示されることもあるのでしょうか?」に対していただいた「正しく表示はされません」という返信への疑問です。
nobu09

2018/08/03 01:47

失礼しました。「真っ白であったり」は正常な画像ではありません。10:41はい、デバッガ上で画像が表示されます。
fuzzball

2018/08/03 01:59

rawData[index] = 255 は真っ白にしているのではないのでしょうか?
nobu09

2018/08/03 05:58

その部分はコメントアウトにしています。ソースコードが間違っており申し訳ありません。
u39ueda

2018/08/03 06:10

実際に試してみたんですが、for文をコメントアウトして動かすとクラッシュしませんか?
nobu09

2018/08/03 10:59

iphone5sで動作確認していますが、ちゃんと動いています。。。念のため、全ソースコードを載せましたのでご確認お願い致します。
guest

回答1

0

ベストアンサー

func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) がメインスレッド以外から呼ばれていませんか?
sampleBufferCallbackQueue にセットされているDispatchQueueから呼ばれるようです。

もしメインスレッドから呼んでいた場合は getByteArrayFromImage() の実行をCVPixelBufferUnlockBaseAddress() の前に移動してみてはどうでしょうか。

追記

実際に試してみましたが以下のような問題があるようです。

  • メインスレッドでfor文をぶん回しているせいで処理に時間がかかっている

→バックグラウンドスレッドで実行させればマシになる(それでもカクカクだけど)
→for文をコメントアウトするならそのままでもOK

  • 真っ白に表示されるのはforで真っ白に塗り潰しているから

→コメントアウト

  • rawDataが画像として使っている間に解放されてしまってクラッシュする

今クラッシュしていないのはよくわからない。CPU使用率100%だから?
→UnsafeMutablePointer.allocate(capacity:)を使ってスコープを抜けても解放されないようにする

rawDataを解放されないようにするのと、for文をコメントアウトすれば動きました。
変更前のコードはコメントアウトしてありますので見比べてください。

Swift

1func getByteArrayFromImage(imageRef: CGImage) -> UIImage { 2 let editedimage:UIImage 3 4 let width_n :Int = Int((imageRef.width)) 5 let height_n :Int = Int((imageRef.height)) 6 7 let data = imageRef.dataProvider!.data 8 let length = CFDataGetLength(data) 9// var rawData = [UInt8](repeating: 0, count: length) 10 let rawData = UnsafeMutablePointer<UInt8>.allocate(capacity: length) 11 12// CFDataGetBytes(data, CFRange(location: 0, length: length), &rawData) 13 CFDataGetBytes(data, CFRange(location: 0, length: length), rawData) 14 // for文で回すならUnsafeMutableBufferPointerを使う 15 let rawDataBuffer = UnsafeMutableBufferPointer<UInt8>(start: rawData, count: length) 16 //for (index, _) in rawData.enumerated() { 17 // rawData[index] = 255 18 //} 19 20// let provider = CGDataProvider(dataInfo: nil, data: &rawData, size: length,releaseData: releaseData) 21 let provider = CGDataProvider(dataInfo: nil, data: rawData, size: length,releaseData: releaseData) 22 let colorSpaceRef = imageRef.colorSpace 23 let bitmapInfo_n = imageRef.bitmapInfo 24 let bitsPerComponent:Int = imageRef.bitsPerComponent 25 let bitsPerPixel = imageRef.bitsPerPixel 26 let bytesPerRow_n = imageRef.bytesPerRow 27 let cgImage = CGImage(width: width_n, height: height_n, bitsPerComponent: bitsPerComponent, bitsPerPixel: bitsPerPixel, bytesPerRow:bytesPerRow_n, space: colorSpaceRef!, bitmapInfo: bitmapInfo_n, provider: provider!, decode: nil, shouldInterpolate: false, intent: .defaultIntent) 28 29 editedimage = UIImage(cgImage:cgImage!, scale:1.0, orientation:UIImageOrientation.right) 30 31 // (2) editedimageには画像データが正常に入っている 32 return editedimage 33 34} 35 36// releaseDataの定義が見当たらなかったので適当に実装しました 37func releaseData(info: UnsafeMutableRawPointer?, data: UnsafeRawPointer, length: Int) { 38 print(#function, info ?? "nil", data, length) 39 data.deallocate() 40}

投稿2018/08/02 06:35

編集2018/08/03 09:19
u39ueda

総合スコア950

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobu09

2018/08/02 12:57

以下の通り、メインスレッドで実行して見ましたが、変化はありませんでした。 ``` func captureOutput() { DispatchQueue.main.async {   let image = self.imageFromSampleBuffer(sampleBuffer: sampleBuffer) previewView.image = image } } ``` また、`getByteArrayFromImage()`を`CVPixelBufferUnlockBaseAddress()`の前に移動しましたが、こちらも変化はありませんでした。
u39ueda

2018/08/03 09:25

回答に追記しました。 for文をコメントアウトするとこちらではクラッシュしたのですが、そちらではそのようなことを言及していないので何か違いがあるのかもしれません。
nobu09

2018/08/03 13:17

ソースコードまで記載していただきありがとうございます。 ソースコード通りに修正したところ、動作しました。 また、こちらではクラッシュしないので、他の部分が違うのかもしれません。 どうもありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問