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

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

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

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

Q&A

解決済

1回答

2062閲覧

画像上のRGB値を取得したいのですが、、、

nekojaras

総合スコア1

Swift

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

0グッド

0クリップ

投稿2020/11/15 03:41

前提・実現したいこと

ネット上のサンプルコードを使って、画像のタップした場所の
RGB値を取得するアプリを試しているのですが、
うまくいきません。

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

画像を読み込んで任意の場所をタップしても
その場所の色と違う数値がでてしまいます。

storyubord上で配置したprintlabelでRGBの数値の表示を、
clorviewでその色のUIViewを表示するようにしています。

該当のソースコード

swift5.1.3

1 2import UIKit 3 4class TapViewController: UIViewController { 5 6 7 @IBOutlet var imageView: UIImageView! 8 @IBOutlet var printlabel: UILabel! 9 @IBOutlet var clorview: UIView! 10 11 var image = UIImage() 12 //表示されている画像のタップ座標用変数 13 var tapPoint = CGPoint(x: 0, y: 0) 14 15 override func viewDidLoad() { 16 } 17 18 override func didReceiveMemoryWarning() { 19 super.didReceiveMemoryWarning() 20 // Dispose of any resources that can be recreated. 21 } 22 23 //imageviewをタップした時に色を判別 24 @IBAction func getImageRGB(_ sender: UITapGestureRecognizer) { 25 26 guard imageView.image != nil else {return} 27 28 //タップした座標の取得 29 tapPoint = sender.location(in: imageView) 30 31 let cgImage = imageView.image?.cgImage! 32 let pixelData = cgImage?.dataProvider!.data 33 let data: UnsafePointer = CFDataGetBytePtr(pixelData) 34 //1ピクセルのバイト数 35 let bytesPerPixel = (cgImage?.bitsPerPixel)! / 8 36 //1ラインのバイト数 37 let bytesPerRow = (cgImage?.bytesPerRow)! 38 print("bytesPerPixel=(bytesPerPixel) bytesPerRow=(bytesPerRow)") 39 //タップした位置の座標にあたるアドレスを算出 40 let pixelAd: Int = Int(tapPoint.y) * bytesPerRow + Int(tapPoint.x) * bytesPerPixel 41 //それぞれRGBAの値をとる 42 let r = CGFloat(Int( CGFloat(data[pixelAd])))///CGFloat(255.0)*100)) / 100 43 let g = CGFloat(Int( CGFloat(data[pixelAd+1])))///CGFloat(255.0)*100)) / 100 44 let b = CGFloat(Int( CGFloat(data[pixelAd+2])))///CGFloat(255.0)*100)) / 100 45 let a = CGFloat(Int( CGFloat(data[pixelAd+3])/CGFloat(255.0)*100)) / 100 46 47 print([r,g,b,a]) 48 //navigationbarに結果を表示 49 let R = "R:" + String(Int(r)) 50 let G = " G:" + String(Int(g)) 51 let B = " B:" + String(Int(b)) 52 let A = " A:" + String(format: "%.1f", a) 53 navigationItem.title = R + G + B + A 54 55 printlabel.text = "([r,g,b,a])" 56 57 clorview.backgroundColor = UIColor(red: CGFloat(r / 255), green: CGFloat(g/255), blue: CGFloat(b/255), alpha: 1.0) 58 } 59} 60 61 62//画像を選択 63extension TapViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { 64 65 //画像を選んだ時の処理 66 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { 67 68 let selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage 69 //サイズを圧縮する 70 // let resizedImage = selectedImage.scale(byFactor: 0.4) 71 72 image = selectedImage 73 74 var imageHeight = image.size.height 75 var imageWidth = image.size.width 76 77 let navigationBarHeight = navigationController?.navigationBar.frame.height 78 let width = self.view.frame.width 79 let height = self.view.frame.height 80 let centerX = self.view.center.x 81 let centerY = self.view.center.y 82 let widthRatio = imageWidth 83 let heightRatio = imageHeight 84 //画像の大きさに応じてiamgeviewのサイズを変える 85 if imageHeight > self.view.frame.height || imageWidth > self.view.frame.width { 86 print("1") 87 imageWidth = width 88 imageHeight = width*heightRatio/widthRatio 89 90 } else if imageHeight > self.view.frame.height { 91 print("2") 92 imageHeight = height 93 imageWidth = height*widthRatio/heightRatio 94 95 } else if imageWidth > self.view.frame.width { 96 print("3") 97 imageWidth = width 98 imageHeight = width*heightRatio/widthRatio 99 100 } else { 101 } 102 103 imageView.contentMode = UIViewContentMode.scaleAspectFill 104 imageView.frame.size = CGSize(width: imageWidth, height: imageHeight) 105 //画像がnavigationbarに被らないようにする 106 if imageHeight/2 > (height/2 - navigationBarHeight!) { 107 print("4") 108 imageView.center = CGPoint(x: centerX, y: centerY + navigationBarHeight!) 109 } else { 110 print("5") 111 imageView.center = CGPoint(x: centerX, y: centerY) 112 } 113 114 imageView.image = image 115 116 picker.dismiss(animated: true, completion: nil) 117 } 118 119 120 121 // 撮影がキャンセルされた時に呼ばれる 122 func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { 123 picker.dismiss(animated: true, completion: nil) 124 } 125 126 func tappedlibrary() { 127 let sourceType:UIImagePickerControllerSourceType = 128 UIImagePickerControllerSourceType.photoLibrary 129 130 if UIImagePickerController.isSourceTypeAvailable( 131 UIImagePickerControllerSourceType.photoLibrary){ 132 // インスタンスの作成 133 let cameraPicker = UIImagePickerController() 134 cameraPicker.sourceType = sourceType 135 cameraPicker.delegate = self 136 self.present(cameraPicker, animated: true, completion: nil) 137 } 138 else{ 139 print("error") 140 141 } 142 } 143 144 func tappedcamera() { 145 let sourceType:UIImagePickerControllerSourceType = 146 UIImagePickerControllerSourceType.camera 147 // カメラが利用可能かチェック 148 if UIImagePickerController.isSourceTypeAvailable( 149 UIImagePickerControllerSourceType.camera){ 150 // インスタンスの作成 151 let cameraPicker = UIImagePickerController() 152 cameraPicker.sourceType = sourceType 153 cameraPicker.delegate = self 154 self.present(cameraPicker, animated: true, completion: nil) 155 156 } 157 else{ 158 print("error") 159 } 160 } 161 162 @IBAction func selecteImageButton(_ sender: UITapGestureRecognizer) { 163 //アラート表示のために 164 let actionSheet = UIAlertController(title: "", message: "写真の選択", preferredStyle: UIAlertControllerStyle.actionSheet) 165 166 let tappedcamera = UIAlertAction(title: "カメラで撮影する", style: UIAlertActionStyle.default, handler: { 167 (action: UIAlertAction!) in 168 self.tappedcamera() 169 }) 170 171 let tappedlibrary = UIAlertAction(title: "ライブラリから選択する", style: UIAlertActionStyle.default, handler: { 172 (action: UIAlertAction!) in 173 self.tappedlibrary() 174 }) 175 176 let cancel = UIAlertAction(title: "キャンセル", style: UIAlertActionStyle.cancel, handler: { 177 (action: UIAlertAction!) in 178 print("キャンセル") 179 }) 180 181 actionSheet.addAction(tappedcamera) 182 actionSheet.addAction(tappedlibrary) 183 actionSheet.addAction(cancel) 184 185 present(actionSheet, animated: true, completion: nil) 186 } 187 188} 189 190

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

Xcode 11.3.1
テスト実機 iPhone11 iOS 13.4.1

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

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

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

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

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

TsukubaDepot

2020/11/15 07:38

参考にされた記事のURLもご質問に追記していただけないでしょうか。 ちなみに、ピクセルの情報を取得する際、アルファ値のみ正規化しているように見えますが、それ以外の値を正規化しない理由はなにかあるのでしょうか。
nekojaras

2020/11/15 08:35

参考にしたのはこちらになります。 https://qiita.com/panzee54/items/89d94f5cffd92358fce3 すみません、試してみて思うように動かなかったので、 アルファ値のみ1.0にしてみて、そのままになっていました。 UIViewは色を見るために自分で付け加えたもので、 変なコードを書いてしまっているかもしれません。 参考URLのものをそのままで動かしてもなぜか正しい色の数値が 出てきませんでした。
guest

回答1

0

ベストアンサー

余計な型キャストがある以外は、基本的な手法としては間違っていないと思います。

正確な色が取れない原因は、スクリーン上でタッチした部分の座標と、UIImage で cgImage として保存されているビットマップ上での座標に対応が取れていないことが原因だと思われます。

ためしに、画像の表示方法を .topLeft のように、ビットマップのピクセルと画素を一対一対応できるように変更して色を調べると、おおよそ想定している色が取得できることは確認しました。

では、根本的に解決するための方法ですが、これは cgImage 構造体で取得できるピクセル数と、表示されているピクセル数を元に、座標変換を行うような処理をさらに追加する必要があるように感じられます。

A bitmap image or image mask.

参考にされているコードだと、デバイスの画素数よりもビットマップの画素数が明らかに少ない場合にはそのまま適用できそうに思いますが、デバイスの画素数よりもビットマップの画素数が大きい場合は、ビットマップの画素数を元に正確に計算する必要があると思います。

UIImage の size 構造体で画像のサイズを計算していますが、それはあくまでも表示上のサイズであって、実ピクセル数ではないかと思います。

The logical dimensions, in points, for the image.

要は、

  • cgImagewidth および height からビットマップ寸法を実ピクセル数として求める
  • ピクセル数が表示領域より大きい場合は、適切にリサイズする。そのとき、実画像のアスペクト比を変更しないように注意する
  • UIImage には .topLeft など、実ピクセル数を元にした表示方法を設定する
  • tapGestureRecognizer で取得した座標を、cgImage の座標に変換する
  • 直接ポインタを操作して RGBA 値をもとめる(これは現状のコードで可能)

という流れにする必要がある、ということになります。

作業量が多いため、上記のことについて全て検証することはできませんが、参考になればと思います。

投稿2020/11/15 23:34

TsukubaDepot

総合スコア5086

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

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

nekojaras

2020/11/16 14:10

検証とアドバイスありがとうございます。 教えられていただいたとおりに、.topLeftで小さいサイズの画像を配置したところ、正しい数値を得ることができました! あとは、大きな画像のリサイズをできるように、作ってみようと思います。 本当にありがとうございました!
TsukubaDepot

2020/11/16 21:15

参考になりそうでよかったです。 最近のiPhone/iPadはRetinaで見た目の解像度と論理的な解像度が違うため、そこも気をつける必要がありそうです(解像度400pxでも、画像は1200pxで表示しているような感じです。とうぜん、元解像度がひくければそれにあわせて自動的に最適な解像度が選ばれます)。 UIImage にも cgImage にも scaleFactor みたいなプロパティがありますので、それで実際に表示させている解像度を調べておくと、もう少し見通しが良くなるかもしれません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問