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

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

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

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

Q&A

解決済

1回答

1511閲覧

このプログラムに画像を小さくするプログラムを付け加えたい。

MashisonJr.

総合スコア11

Swift

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

0グッド

0クリップ

投稿2019/11/19 07:49

編集2019/11/22 04:46

前提・実現したいこと

カメラを使って画像を撮った時に、その撮った画像をすぐに小さくできるようにしたい。

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

IphoneXのカメラで撮った画像を使うと、IphoneXのディスプレイにはきれいに画像が表示されるが、表示されている画像とImageViewの大きさがあっていないのか、座標とRGB値がうまく取れない。
そのためカメラで撮った時にすぐに撮った画像のサイズを小さくできるようにしたい。

該当のソースコード

Swift5

import UIKit class TapViewController: UIViewController { @IBOutlet var imageView: UIImageView! var image = UIImage() //表示されている画像のタップ座標用変数 var tapPoint = CGPoint(x: 0, y: 0) override func viewDidLoad() { } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } //imageviewをタップした時に色を判別 @IBAction func getImageRGB(_ sender: UITapGestureRecognizer) { guard imageView.image != nil else {return} //タップした座標の取得 tapPoint = sender.location(in: imageView) let cgImage = imageView.image?.cgImage! let pixelData = cgImage?.dataProvider!.data let data: UnsafePointer = CFDataGetBytePtr(pixelData) //1ピクセルのバイト数 let bytesPerPixel = (cgImage?.bitsPerPixel)! / 8 //1ラインのバイト数 let bytesPerRow = (cgImage?.bytesPerRow)! print("bytesPerPixel=(bytesPerPixel) bytesPerRow=(bytesPerRow)") //タップした位置の座標にあたるアドレスを算出 let pixelAd: Int = Int(tapPoint.y) * bytesPerRow + Int(tapPoint.x) * bytesPerPixel //それぞれRGBAの値をとる let r = Int( CGFloat(data[pixelAd])) let g = Int( CGFloat(data[pixelAd+1])) let b = Int( CGFloat(data[pixelAd+2])) let a = CGFloat(Int( CGFloat(data[pixelAd+3])/CGFloat(255.0)*100)) / 100 print([r,g,b,a]) //navigationbarに結果を表示 let R = "R:" + String(Int(r)) let G = " G:" + String(Int(g)) let B = " B:" + String(Int(b)) let A = " A:" + String(format: "%.1f", a) navigationItem.title = R + G + B + A } } //画像を選択 extension TapViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { //画像を選んだ時の処理 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { let selectedImage = info[.originalImage] as! UIImage //サイズを圧縮する // let resizedImage = selectedImage.scale(byFactor: 0.4) image = selectedImage var imageHeight = image.size.height var imageWidth = image.size.width let navigationBarHeight = navigationController?.navigationBar.frame.height let width = self.view.frame.width let height = self.view.frame.height let centerX = self.view.center.x let centerY = self.view.center.y let widthRatio = imageWidth let heightRatio = imageHeight //画像の大きさに応じてiamgeviewのサイズを変える if imageHeight > self.view.frame.height || imageWidth > self.view.frame.width { imageWidth = width imageHeight = width*heightRatio/widthRatio } else if imageHeight > self.view.frame.height { imageHeight = height imageWidth = height*widthRatio/heightRatio } else if imageWidth > self.view.frame.width { imageWidth = width imageHeight = width*heightRatio/widthRatio } else { } imageView.contentMode = UIView.ContentMode.scaleAspectFill imageView.frame.size = CGSize(width: imageWidth, height: imageHeight) //画像がnavigationbarに被らないようにする if imageHeight/2 > (height/2 - navigationBarHeight!) { imageView.center = CGPoint(x: centerX, y: centerY + navigationBarHeight!) } else { imageView.center = CGPoint(x: centerX, y: centerY) } imageView.image = image picker.dismiss(animated: true, completion: nil) } // 撮影がキャンセルされた時に呼ばれる func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true, completion: nil) } func tappedlibrary() { let sourceType:UIImagePickerController.SourceType = UIImagePickerController.SourceType.photoLibrary if UIImagePickerController.isSourceTypeAvailable( UIImagePickerController.SourceType.photoLibrary){ // インスタンスの作成 let cameraPicker = UIImagePickerController() cameraPicker.sourceType = sourceType cameraPicker.delegate = self self.present(cameraPicker, animated: true, completion: nil) } else{ print("error") } } func tappedcamera() { let sourceType:UIImagePickerController.SourceType = UIImagePickerController.SourceType.camera // カメラが利用可能かチェック if UIImagePickerController.isSourceTypeAvailable( UIImagePickerController.SourceType.camera){ // インスタンスの作成 let cameraPicker = UIImagePickerController() cameraPicker.sourceType = sourceType cameraPicker.delegate = self self.present(cameraPicker, animated: true, completion: nil) } else{ print("error") } } @IBAction func selecteImageButton(_ sender: UITapGestureRecognizer) { //アラート表示のために let actionSheet = UIAlertController(title: "", message: "写真の選択", preferredStyle: UIAlertController.Style.actionSheet) let tappedcamera = UIAlertAction(title: "カメラで撮影する", style: UIAlertAction.Style.default, handler: { (action: UIAlertAction!) in self.tappedcamera() }) let tappedlibrary = UIAlertAction(title: "ライブラリから選択する", style: UIAlertAction.Style.default, handler: { (action: UIAlertAction!) in self.tappedlibrary() }) let cancel = UIAlertAction(title: "キャンセル", style: UIAlertAction.Style.cancel, handler: { (action: UIAlertAction!) in print("キャンセル") }) actionSheet.addAction(tappedcamera) actionSheet.addAction(tappedlibrary) actionSheet.addAction(cancel) present(actionSheet, animated: true, completion: nil) } }

試したこと

画像のリサイズなどを自分で加えたりしたがうまくいかなかった。

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

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

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

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

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

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

takabosoft

2019/11/19 07:56

}``` ←中括弧の後ろに改行入れてください。 あと、現状で何が問題になっているのかが質問文から読み取れません。 質問欄は編集できます。
eytyet

2019/11/22 04:13

ソースコード全体の前と後ろに、```だけの行を追加してください。そうすると、ソースコードのインデントやキーワードの色付けなどがされて読みやすくなります。今の状態ですと読むのが難しいです。 ```swift ソースコード ``` ↑こんな感じで、最初の方にswiftとつけるともっと良いです。
guest

回答1

0

ベストアンサー

本当にやりたい事は、画像上の点の色を調べたい、という事ですね。

(1) 座標変換すればリサイズしなくても可能です。
tapPointには「表示されているimageViewの画面上のサイズの点」の座標が入るので、それを元に元画像上の座標を割り出します。

  • imageViewcontentModescaleToFillにします。

  • xとyそれぞれ"比"を使って変換します。let x = tapPoint.x / imageView.frame.width * cgImage.widthという感じで。

imageViewの表示がおかしくなると思うので、imageViewの縦横比が元画像と同じになるように、Auto Layoutを設定してください。

(2) RGBAの並び
RGBAの並びは固定ではありません。試した限りでは、assetに入れたJPEG画像のままではRGBAでしたが、後述のリサイズを通した画像はBGRAでした。これは、CGImage.bitmapInfoの情報から判別可能です。
また、1ピクセルが8ビットとも限りません。CGImage.bitPerComponentで分かります。(殆どのケースは8ビット固定でも大丈夫だと思います)

座標変換を反映したonImageTapはこちらです。置き換えて下さい。

swift

1@IBAction func onImageTap(_ sender: UITapGestureRecognizer) { 2 guard let image = imageView.image else { return } 3 let pointOnImageView = sender.location(in: imageView) 4 let x = pointOnImageView.x / imageView.frame.width * image.size.width 5 let y = pointOnImageView.y / imageView.frame.height * image.size.height 6 7 if let color = image.color(of: CGPoint(x: x, y: y)) { 8 label.text = String(format: "%4dx%4d - (r:%3d, g:%3d, b:%3d, a:", Int(x), Int(y), color.red, color.green, color.blue) + String(describing: color.alpha) + ")" 9 } 10}

点の取得を関数にしました。どこかのファイルに、以下も追加してください。

swift

1extension UIImage { 2 /// Image上の点の色を返す。cgImageをもち、8bit/色だけ有効。alphaのみは非対応でnilを返す。 3 /// - Parameter point: Image上の色を調べたい点の座標。 4 /// - Returns: 不明のときはnilが返る。各色の値は0-255。alphaがない場合nilを返す。 5 func color(of point: CGPoint) -> (red:Int, green:Int, blue:Int, alpha:Int?)? { 6 guard let cgImage = self.cgImage else { return nil } 7 guard let pixelData = cgImage.dataProvider?.data else { return nil } 8 guard let data = CFDataGetBytePtr(pixelData) else { return nil } 9 guard 0 <= point.x && Int(point.x) < cgImage.width && 10 0 <= point.y && Int(point.y) < cgImage.height else { return nil } 11 guard cgImage.bitsPerComponent == 8 else { return nil } 12 print(CGImageAlphaInfo.premultipliedLast.rawValue, CGImageAlphaInfo.premultipliedFirst.rawValue) 13 let byteOrder = cgImage.bitmapInfo.intersection(.byteOrderMask) 14 guard byteOrder == [] || byteOrder == .byteOrder32Big || byteOrder == .byteOrder32Little else { return nil } 15 16 //タップした位置の座標にあたるアドレスを算出 17 let address = Int(point.y) * cgImage.bytesPerRow + Int(point.x) * cgImage.bitsPerPixel / 8 18 19 //それぞれRGBAの値をとる 20 let alphaInfo = CGImageAlphaInfo(rawValue: cgImage.bitmapInfo.intersection(.alphaInfoMask).rawValue) 21 var offset: (red:Int, green:Int, blue:Int, alpha:Int?) 22 if byteOrder == .byteOrder32Little { 23 if [.first, .noneSkipFirst, .premultipliedFirst].contains(alphaInfo) { 24 offset = (2, 1, 0, 3) 25 } else { 26 offset = (3, 2, 1, 0) 27 } 28 } else { // [] || .byteOrder32Big 29 if [.first, .noneSkipFirst, .premultipliedFirst].contains(alphaInfo) { 30 offset = (1, 2, 3, 0) 31 } else { 32 offset = (0, 1, 2, 3) 33 } 34 } 35 // alphaがない場合 36 if [CGImageAlphaInfo.none, .alphaOnly, .noneSkipFirst, .noneSkipLast].contains(alphaInfo) { 37 offset.alpha = nil 38 } 39 40 let r = Int(data[address + offset.red]) 41 let g = Int(data[address + offset.green]) 42 let b = Int(data[address + offset.blue]) 43 let a = offset.alpha != nil ? Int(data[address + offset.alpha!]) : nil 44 return (r, g, b, a) 45 } 46}

なお、正しく点を調べられるようになっても、一点だけだと見た目とはだいぶ違う色が取れたりします。今回のように大きい画像を縮小して表示していると、全然違う色を読んでくる場合も多いでしょう。
その場合は縮小は一つの解決策になるかと思います。

リサイズする関数の例はこちらです。

swift

1 func resize(image: UIImage, ratio: CGFloat) -> UIImage? { 2 let newSize = CGSize(width: image.size.width * ratio, height: image.size.height * ratio) 3 UIGraphicsBeginImageContext(newSize) 4 image.draw(in: CGRect(origin: CGPoint.zero, size: newSize)) 5 let resizedImage = UIGraphicsGetImageFromCurrentImageContext() 6 UIGraphicsEndImageContext() 7 return resizedImage 8 } 9

ライブラリを使うのも近道かなと思います。
私は使った事がないので恐縮ですが、この辺りを参考になさってはどうでしょうか。
https://qiita.com/koher/items/7dc1aa10755b79102539

投稿2019/11/22 06:56

編集2019/11/22 13:28
eytyet

総合スコア803

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

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

MashisonJr.

2019/11/22 07:22

このプログラムにそれらを追加しようとしたら、どこにどのように追加したらいいんでしょうか? よければ教えていただきたいです。
eytyet

2019/11/22 07:33

ここまでコードが書けていたら難しいことではないと思うのですが、どのあたりが分かりにくいでしょうか。
MashisonJr.

2019/11/22 07:46

すいません。 見様見真似でしてみたものの、IphoneXの画像だとうまく座標が合わなくて困ってます。 色々調べて自分で改良したのですが、うまくいかないです。 座標の部分とRGBAの部分を詳しく知りたいです。よろしくお願いいたします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問