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

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

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

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

iOS

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

Swift

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

Q&A

解決済

1回答

5633閲覧

【Swift/OpenCV】アプリがクラッシュし「Terminated due to memory issue」が表示される

yamaji1108

総合スコア19

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

iOS

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

Swift

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

0グッド

1クリップ

投稿2020/04/18 14:41

編集2020/04/19 15:00

前提・実現したいこと

https://qiita.com/koooootake/items/8aed4ee758548689f0a6
こちらの方が公開している「Portrait-without-Depth-ios」というアプリをクローンし、実機で試していた際のエラーです。
こちらのアプリは、読み込んだ画像に対して、被写体と背景を分離して、背景だけをボヤけさせるという機能があります。

画像(2448×2448、1.1MB)を読み込むまでは問題なく出来たのですが、その後、「Done」ボタンを押下した際に、しばらくするとアプリがクラッシュしてしまいます。
![イメージ説明]

エラーメッセージと該当しているソースコード(文字数の関係で一部をカットしています。)を載せます。
217行目の
return OpenCVManager.shared()?.doGrabCut(img, foregroundRect: rect, iterationCount: 1)
で__doGrabCut__メソッドが問題のコードだと思います。

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

Message from debugger: Terminated due to memory issue.

該当のソースコード(ViewController.swift)

Swift

1// Copyright © 2018年 koooootake. All rights reserved. 2// 3 4import UIKit 5import CropViewController 6import AVFoundation 7 8class ViewController: UIViewController, UINavigationControllerDelegate { 9 10 let kGradientLayerName = "Gradient" 11 12 var viewModel: ViewModel = ViewModel() 13 14 //base 15 @IBOutlet weak var imageSizeLabel: UILabel! 16 @IBOutlet weak var baseView: UIView! 17 @IBOutlet weak var sourceImageView: UIImageView! 18 19 //segment 20 private var selectedRect: CGRect? 21 @IBOutlet weak var maskImageView: UIImageView! 22 @IBOutlet weak var drawImageView: DrawView! 23 @IBOutlet weak var drawPenSizeSlider: UISlider! 24 25 //result 26 private var resultImage: UIImage? 27 private var blurWithoutGradientImage: UIImage? 28 private var inpaintingImage: UIImage? 29 @IBOutlet weak var resultImageView: UIImageView! 30 @IBOutlet weak var blurSizeSlider: UISlider! 31 32 //editView 33 @IBOutlet weak var indicatorBaseView: UIView! 34 @IBOutlet weak var indicatorView: UIActivityIndicatorView! 35 @IBOutlet weak var editView: UIView! 36 @IBOutlet var segmentEditView: UIView! 37 @IBOutlet var resultEditView: UIView! 38 @IBOutlet var dofEditView: UIView! 39 @IBOutlet weak var safeAreaView: UIView! 40 41 private lazy var tapFeedbackGenerator: UIImpactFeedbackGenerator = { 42 let generator = UIImpactFeedbackGenerator(style: .light) 43 generator.prepare() 44 return generator 45 }() 46 47 override func viewDidLoad() { 48 super.viewDidLoad() 49 reload(status: .load) 50 setupIndicatorBaseView() 51 } 52 53 private func setupIndicatorBaseView() { 54 let visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) 55 visualEffectView.frame = indicatorBaseView.bounds 56 indicatorBaseView.addSubview(visualEffectView) 57 indicatorBaseView.layer.cornerRadius = 4 58 indicatorBaseView.layer.masksToBounds = true 59 indicatorBaseView.bringSubviewToFront(indicatorView) 60 indicatorBaseView.alpha = 0 61 } 62 63 private func reload(status: ViewModel.Status) { 64 viewModel.reload(status: status) 65 maskImageView.isHidden = viewModel.isHiddenSegmentView 66 drawImageView.isHidden = viewModel.isHiddenSegmentView 67 resultImageView.isHidden = viewModel.isHiddenResultImageView 68 navigationItem.title = viewModel.navigationTitle 69 safeAreaView.backgroundColor = viewModel.viewBackgroundColor 70 71 for view in editView.subviews { 72 view.removeFromSuperview() 73 } 74 75 let editContentView: UIView 76 switch status { 77 case .load: 78 //do nothing 79 return 80 case .segment: 81 editContentView = segmentEditView 82 case .result: 83 editContentView = resultEditView 84 case .dof: 85 editContentView = dofEditView 86 } 87 88 editView.addSubview(editContentView) 89 editView.addConstraintsFitParentView(editContentView) 90 } 91 92 private func reset() { 93 resetDraw() 94 reload(status: .load) 95 } 96 97 private func resetDraw() { 98 drawImageView.reset() 99 OpenCVManager.shared()?.resetManager() 100 } 101 102 private func startLoading() { 103 UIApplication.shared.beginIgnoringInteractionEvents() 104 indicatorView.startAnimating() 105 indicatorBaseView.isHidden = false 106 UIView.animate(withDuration: 0.1, animations: { 107 self.indicatorBaseView.alpha = 1 108 }) 109 } 110 111 private func stopLoading() { 112 UIApplication.shared.endIgnoringInteractionEvents() 113 UIView.animate(withDuration: 0.3, animations: { 114 self.indicatorBaseView.alpha = 0 115 }, completion: { _ in 116 self.indicatorView.stopAnimating() 117 self.indicatorBaseView.isHidden = true 118 }) 119 } 120} 121 122///1. 画像取得 + 前景矩形選択 123extension ViewController { 124 125 @IBAction func imagePickerButtonDidTap(_ sender: Any) { 126 if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) { 127 let pickerC = UIImagePickerController() 128 pickerC.sourceType = .photoLibrary 129 pickerC.delegate = self 130 present(pickerC, animated: true, completion: nil) 131 } else { 132 UIAlertController.show(title: "フォトライブラリの利用が許可されていません????", message: nil) 133 } 134 } 135 136 @IBAction func cropButtonDidTap(_ sender: Any) { 137 guard let img = sourceImageView.image else { 138 assertionFailure() 139 return 140 } 141 showCropViewController(image: img) 142 } 143 144 private func showCropViewController(image: UIImage) { 145 let cropVC = CropViewController(image: image) 146 cropVC.rotateButtonsHidden = true 147 cropVC.title = "Rough outline of the subject" 148 cropVC.delegate = self 149 if let selectedRect = selectedRect { 150 cropVC.imageCropFrame = selectedRect 151 } else { 152 cropVC.aspectRatioPreset = .presetSquare 153 } 154 present(cropVC, animated: true, completion: nil) 155 } 156} 157 158extension ViewController: UIImagePickerControllerDelegate { 159 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 160 guard let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { 161 assertionFailure() 162 return 163 } 164 //画像サイズ縮小 165 let maxSize: CGFloat = 1800 166 let imageMaxSize = max(image.size.width, image.size.height) 167 let scale = imageMaxSize > maxSize ? maxSize / imageMaxSize : 1 168 let resizeImg = image.scale(ratio: scale) 169 sourceImageView.image = resizeImg 170 imageSizeLabel.text = "(Int(resizeImg.size.width)) x (Int(resizeImg.size.height))" 171 selectedRect = nil 172 reset() 173 self.dismiss(animated: true, completion: { 174 self.showCropViewController(image: resizeImg) 175 }) 176 } 177 178} 179 180extension ViewController: CropViewControllerDelegate { 181 func cropViewController(_ cropViewController: CropViewController, didCropImageToRect rect: CGRect, angle: Int) { 182 selectedRect = rect 183 resetDraw() 184 startLoading() 185 cropViewController.dismiss(animated: true, completion: { 186 self.doGrabCut() 187 self.stopLoading() 188 }) 189 } 190} 191 192///2. 矩形でGrabCutする 193extension ViewController { 194 private func doGrabCut() { 195 maskImageView.image = doGrabCutWithRect() 196 reload(status: .segment) 197 } 198 199 private func doGrabCutWithRect() -> UIImage? { 200 guard let img = sourceImageView.image else { 201 assertionFailure() 202 return nil 203 } 204 205 //矩形取得 206 let rect: CGRect 207 if let selectedR = selectedRect { 208 if selectedR.size == img.size {//矩形が画像と同じサイズのとき僅かに小さくして入力 209 rect = CGRect(origin: CGPoint(x: selectedR.origin.x + 1, y: selectedR.origin.y + 1), size: CGSize(width: selectedR.size.width - 2, height: selectedR.size.height - 2)) 210 } else { 211 rect = selectedR 212 } 213 } else { 214 rect = CGRect(origin: CGPoint(x: 1, y: 1), size: CGSize(width: img.size.width - 2, height: img.size.height - 2)) 215 } 216 217 return OpenCVManager.shared()?.doGrabCut(img, foregroundRect: rect, iterationCount: 1) 218 } 219} 220 221///3.マスクでGrabCutする 222extension ViewController { 223 224} 225 226

該当のソースコード(OpenCVManager.h)

OpenCV

1 2#ifndef OpenCVManager_h 3#define OpenCVManager_h 4 5#import <Foundation/Foundation.h> 6#import <UIKit/UIKit.h> 7 8@interface OpenCVManager : NSObject 9+ (OpenCVManager*)sharedManager; 10 11-(UIImage*)doGrabCut:(UIImage*)sourceImage foregroundRect:(CGRect) rect iterationCount:(int)iterCount; 12-(UIImage*)doGrabCut:(UIImage*)sourceImage markersImage:(UIImage*)maskImage iterationCount:(int) iterCount; 13-(UIImage*)doBlur:(CGFloat)blurSize isUpdatedSegmentation:(BOOL)isUpdatedSegmentation gradientMaskImage:(UIImage*)gradientMaskImage; 14-(UIImage*)inpaintingImage; 15-(UIImage*)blurWithoutGradientImage; 16-(void) resetManager; 17@end 18 19#endif /* OpenCVManager_h */ 20

試したこと

ブレイクポイントを付け、動作を確認しました。
イメージ説明
すると、「EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=650 MB, unused=0x0)」のエラーメッセージが表示されました。
これは、実機(iPhone6)のメモリの限界である650MBを超えてしまったことによってクラッシュしてしまうということでしょうか。

試しに他のサイズが小さい画像(1280×1280、271KB)で実行した時は、上記のようなクラッシュは起こりませんでした。

ソースコードを修正することによって、メモリの負荷を抑えクラッシュを防ぐことは可能でしょうか。
稚拙な文章で失礼いたしますが、長時間調べても解決できなかったため、こちらで質問させていただきました。
ご教授のほど、よろしくお願いします。

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

Xcode Version 10.3 (10G8)
iOS 12.4.6
Swift Version 5.0.1

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

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

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

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

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

hoshi-takanori

2020/04/19 14:40 編集

1024×1024、1.1MB (圧縮サイズ?) の画像を処理するのに 650MB も必要なんですね。 でも、1280×1280、271MB は大丈夫なんですか? なんか計算が合わない気がしますが。 とりあえず、画像が大きければ縮小するとかでしょうか。
yamaji1108

2020/04/19 14:58

大変失礼いたしました。 エラーとなる方が、2448×2448の1.1MB 正常動作する方が、1280×1280の271KB でした。早急に修正いたします。
hoshi-takanori

2020/04/20 22:08

OpenCV は詳しくないのでよく分かりませんが、2448x2448 の画像が 1.1MB、1280×1280 の画像が 271kB というのはあくまでも JPEG や PNG などで圧縮した結果であって、実際に処理を行う場合にはメモリ上に展開する必要がありますのでもっと大きくなります。また、実際の処理を行う際にはたぶん元の画像を何度もコピーしていると思いますので、画像が大きければ大きいほどメモリをたくさん使うのではないかと。 で、ソースをよく見たら UIImagePickerControllerDelegate のところで画像を縮小しているので、let maxSize: CGFloat = 1800 の値を小さくすればいいのでは。
yamaji1108

2020/04/21 01:28

ありがとうございます!ご指摘の通り、let maxSize: CGFloat = 1800の値を1400に変更したら、メモリー負荷が400MBちょっとに抑えられ、エラー解消いたしまいた! 本当に助かりました。
yamaji1108

2020/04/21 01:31

Hoshi-takanori様 回答の方にも記載をお願いしてもよろしいでしょうか。 アンサーとしてこちらの質問をクローズしたいと思います。
guest

回答1

0

ベストアンサー

画像が大きすぎるのだと思います。UIImagePickerControllerDelegate のところで画像を縮小しているので、let maxSize: CGFloat = 1800 の値を小さくすればいいのではないでしょうか。

swift

1extension ViewController: UIImagePickerControllerDelegate { 2 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { 3 //略 4 //画像サイズ縮小 5 let maxSize: CGFloat = 1800

投稿2020/04/21 01:52

hoshi-takanori

総合スコア7901

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問