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

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

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

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

Q&A

1回答

276閲覧

Swiftを用いたiPhone向け画像二値化アプリの実装

hiyorin

総合スコア2

Swift

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

0グッド

0クリップ

投稿2023/05/25 05:20

実現したいこと

Swiftを用いたiPhone向け画像二値化アプリの実装

前提

プログラミング初心者のため、コード、文章共に稚拙な部分があると思いますがお力添えをいただければ幸いです。
Swiftを用いたiPhone向け画像二値化アプリを作成しています。
特定の画像の二値化の処理を行った際、後述のようなエラーメッセージが発生しました。

使用している画像は以下の通りです。
↓Lena.jpg
イメージ説明

↓cat.jpgイメージ説明

↓Earth.jpgイメージ説明

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

cat.jpgをフォトライブラリーから読み込んだ際、次のエラーメッセージが発生します。

Thread 1: Fatal error: Index out of range

該当のソースコード

Swift

1// ContentView.swift 2 3import SwiftUI 4import UIKit 5import PhotosUI 6 7extension UIImage { 8 func createBinarizedImage(r:[CGFloat], g: [CGFloat], b:[CGFloat], a:[CGFloat], threshold:CGFloat) -> UIImage{ 9 UIGraphicsBeginImageContextWithOptions(size, false, 0) 10 let wid:Int = Int(size.width) 11 let hei:Int = Int(size.height) 12 for w in 0..<wid { 13 for h in 0..<hei { 14 let index = (w * wid) + h 15 var color = 0.2126 * r[index] + 0.7152 * g[index] + 0.0722 * b[index] 16 if color > threshold / 100 { 17 color = 255 18 } else { 19 color = 0 20 } 21 UIColor(red: color, green: color, blue: color, alpha: a[index]).setFill() 22 let drawRect = CGRect(x: w, y: h, width: 1, height: 1) 23 UIRectFill(drawRect) 24 draw(in: drawRect, blendMode: .destinationIn, alpha: 1) 25 } 26 } 27 let binarizeImage = UIGraphicsGetImageFromCurrentImageContext()! 28 UIGraphicsEndImageContext() 29 return binarizeImage 30 } 31 32 // resize image 33 func resize () -> UIImage? { 34 let rate = 350.0 / self.size.width 35 let rect = CGRect(x: 0, 36 y: 0, 37 width: self.size.width * rate, 38 height: self.size.height * rate) 39 40 UIGraphicsBeginImageContext (rect.size) 41 self.draw(in: rect) 42 let image = UIGraphicsGetImageFromCurrentImageContext() 43 UIGraphicsEndImageContext () 44 return image 45 } 46} 47 48class ViewController: UIViewController { 49 @IBOutlet weak var image: UIImageView! 50 51 var r:[CGFloat] = [] 52 var g:[CGFloat] = [] 53 var b:[CGFloat] = [] 54 var a:[CGFloat] = [] 55 56 var threshold:CGFloat = 0.0 57 58 override func viewDidLoad() { 59 super.viewDidLoad() 60 let img = UIImage(named: "Lena")! 61 if let pixelBuffer = PixelBuffer(uiImage: img) { 62 for x in 0..<pixelBuffer.width { 63 for y in 0..<pixelBuffer.height { 64 r.append(pixelBuffer.getRed(x: x, y: y)) 65 g.append(pixelBuffer.getBlue(x: x, y: y)) 66 b.append(pixelBuffer.getGreen(x: x, y: y)) 67 a.append(pixelBuffer.getAlpha(x: x, y: y)) 68 } 69 } 70 } else { 71 print("image not format") 72 } 73 image.image = img.createBinarizedImage(r: r, g: g, b: b, a: a, threshold: threshold) 74 } 75} 76 77class PixelBuffer { 78 private var pixelData: Data 79 var width: Int 80 var height: Int 81 private var bytesPerRow: Int 82 private let bytesPerPixel = 4 //1ピクセルが4バイトのデータしか扱わない 83 84 init?(uiImage: UIImage) { 85 guard let cgImage = uiImage.cgImage, 86 //R,G,B,A各8Bit 87 cgImage.bitsPerComponent == 8, 88 //1 pixelが32bit 89 cgImage.bitsPerPixel == bytesPerPixel * 8 else { 90 return nil 91 } 92 pixelData = cgImage.dataProvider!.data! as Data 93 width = cgImage.width 94 height = cgImage.height 95 bytesPerRow = cgImage.bytesPerRow 96 } 97 98 func getRed(x: Int, y: Int) -> CGFloat { 99 let pixelInfo = bytesPerRow * y + x * bytesPerPixel 100 let r = CGFloat(pixelData[pixelInfo]) / CGFloat(255.0) 101 102 return r 103 } 104 func getGreen(x: Int, y: Int) -> CGFloat { 105 let pixelInfo = bytesPerRow * y + x * bytesPerPixel 106 let green = CGFloat(pixelData[pixelInfo+1]) / CGFloat(255.0) 107 108 return green 109 } 110 func getBlue(x: Int, y: Int) -> CGFloat { 111 let pixelInfo = bytesPerRow * y + x * bytesPerPixel 112 let blue = CGFloat(pixelData[pixelInfo+2]) / CGFloat(255.0) 113 114 return blue 115 } 116 func getAlpha(x: Int, y: Int) -> CGFloat { 117 let pixelInfo = bytesPerRow * y + x * bytesPerPixel 118 let alpha = CGFloat(pixelData[pixelInfo+3]) / CGFloat(255.0) 119 return alpha 120 } 121} 122 123 124 125struct ContentView: View { 126 127 @State var isPhotolibrary = false 128 129 @State var isShowAction = false 130 @State var isShowSheet = false 131 132 @State var bin = 50.0 133 134 @State var isEditing = false 135 136 @State var captureImage: UIImage? = nil 137 @State var img = UIImage(named: "Lena") 138 139 @State var img_bin :UIImage? 140 141 142 init() { 143 if let pixelBuffer = PixelBuffer(uiImage: img!) { 144 var r = [CGFloat]() 145 var g = [CGFloat]() 146 var b = [CGFloat]() 147 var a = [CGFloat]() 148 for x in 0..<pixelBuffer.width { 149 for y in 0..<pixelBuffer.height { 150 r.append(pixelBuffer.getRed(x: x, y: y)) 151 g.append(pixelBuffer.getGreen(x: x, y: y)) 152 b.append(pixelBuffer.getBlue(x: x, y: y)) 153 a.append(pixelBuffer.getAlpha(x: x, y: y)) 154 } 155 } 156 _img_bin = State(initialValue: img!.createBinarizedImage(r: r, g: g, b: b, a: a, threshold:bin)) 157 } else { 158 print("image not format") 159 } 160 } 161 162 // --- img_binを設定し直しすためのメソッドです。 163 func setimgbin() { 164 //img = img?.resize() 165 if let pixelBuffer = PixelBuffer(uiImage: img!) { 166 var r = [CGFloat]() 167 var g = [CGFloat]() 168 var b = [CGFloat]() 169 var a = [CGFloat]() 170 for x in 0..<pixelBuffer.width { 171 for y in 0..<pixelBuffer.height { 172 r.append(pixelBuffer.getRed(x: x, y: y)) 173 g.append(pixelBuffer.getGreen(x: x, y: y)) 174 b.append(pixelBuffer.getBlue(x: x, y: y)) 175 a.append(pixelBuffer.getAlpha(x: x, y: y)) 176 } 177 } 178 // --- createBinarizedImageが遅いためスライダーが滑らかになりません 179 self.img_bin = img!.createBinarizedImage(r: r, g: g, b: b, a: a, threshold:bin) 180 } else { 181 print("image not format") 182 } 183 } 184 185 var body: some View { 186 187 VStack{ 188 Image(uiImage:img!) 189 if let unwrap_img_bin = img_bin { 190 Image(uiImage:unwrap_img_bin) 191 } 192 else{ 193 Text("アンラップ失敗") 194 } 195 Slider( 196 value: $bin, 197 in: 0...100, 198 step: 1.0, 199 onEditingChanged: { 200 editing in 201 isEditing = editing 202 setimgbin() 203 } 204 ) 205 206 Text("\(bin, specifier: "%.0f")") 207 .foregroundColor(isEditing ? .red : .blue) 208 209 Button(action: { 210 211 //isPhotolibrary = true 212 isShowAction = true 213 214 }){ 215 Text("Act") 216 .frame(maxWidth: .infinity) 217 .frame(height: 50) 218 .multilineTextAlignment(.center) 219 .background(Color.blue) 220 .foregroundColor(Color.white) 221 } 222 .padding() 223 .sheet(isPresented: $isShowSheet) { 224 225 if isPhotolibrary { 226 PHPickerView(isShowSheet: $isShowSheet, captureImage: $img)//写真のサイズが大きすぎる 227 }else{ 228 ImagePickerView(isShowSheet: $isShowSheet, captureImage: $img) 229 } 230 231 } 232 }.actionSheet(isPresented: $isShowAction){ 233 ActionSheet(title: Text("確認"), 234 message: Text("選択してください"), 235 buttons: [ 236 .default(Text("カメラ"), action: { 237 isPhotolibrary = false 238 239 if UIImagePickerController.isSourceTypeAvailable(.camera){ 240 241 print("カメラは利用できます") 242 isShowSheet = true 243 244 }else { 245 print("カメラは利用できません") 246 } 247 248 }), 249 .default(Text("フォトライブラリー"), action: { 250 isPhotolibrary = true 251 isShowSheet = true 252 print("フォトライブラリー") 253 }), 254 .cancel(), 255 ]) 256 } 257 } 258 259 260 struct ContentView_Previews: PreviewProvider { 261 static var previews: some View { 262 ContentView() 263 } 264 } 265} 266 267

試したこと

・Lenaの二値化は問題ありませんでした。
・フォトライブラリーからEarth.jpgを選択した場合、エラーメッセージは表示されず、二値化も問題なく行うことができました。

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

Xcode(Version 14.3)
MacBook Pro 13 (macOS 13.3.1)
iPhone11 (iOS 16.4.1)

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

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

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

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

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

hiyorin

2023/05/25 05:21

追記:文字数の関係で本文に記載できなかったため、ここにソースコードの続きを記載します // ImagePickerView.swift import SwiftUI struct ImagePickerView: UIViewControllerRepresentable { @Binding var isShowSheet: Bool @Binding var captureImage: UIImage? class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate{ let parent: ImagePickerView init(_ parent: ImagePickerView){ self.parent = parent } func imagePickerController( _ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { if let originalImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage { parent.captureImage = originalImage } parent.isShowSheet = false } func imagePickerControllerDidCancel( _ picker: UIImagePickerController) { parent.isShowSheet = false } } func makeCoordinator() -> Coordinator{ Coordinator(self) } func makeUIViewController( context: UIViewControllerRepresentableContext<ImagePickerView>)-> UIImagePickerController { let myImagePickerController = UIImagePickerController() myImagePickerController.sourceType = .camera myImagePickerController.delegate = context.coordinator return myImagePickerController } func updateUIViewController( _ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePickerView>) { } }
hoshi-takanori

2023/05/25 08:53

let index = (w * wid) + h じゃなくて let index = (h * wid) + w なような…。
hiyorin

2023/05/25 11:55

ご回答ありがとうございます!!! 早速14行目を let index = (h * wid) + w 変更してみましたが、二値化処理後の画像が90°左に回転(Lenaで言うと、頭が左になっている)してしまいました。 上記の問題も解決されていないので、違うところに原因がありそうです、、、
hoshi-takanori

2023/05/25 17:55

じゃあ let index = (w * hei) + h かな…。
hiyorin

2023/05/26 06:21

ありがとうございます!!!無事に作動しました!!!
guest

回答1

0

上記(コメント)の通りに修正したところ、問題なく作動しました。

投稿2023/07/20 08:22

hiyorin

総合スコア2

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問