iPhone
Xcode
Swift

[Swift]画像をタップした場所のRGB値をとる

はじめに

今回は、ImageViewに表示した画像のタップした場所からRBG値を取ってみたいと思います。

デモ動画

やること

  • ImageViewに表示した画像のタプした場所からRGB値を取り、NavigationBarに表示する。

やらないこと

  • 画像の拡大、縮小
  • タップした場所の表示

環境

  • Swift4
  • Xcode 9.4.1

StoryBoard

スクリーンショット 2018-08-05 21.18.04.png

左上にカメラボタンを配置するためにNavigation Controllerを設置。
それと画像を表示するImageVIewを置きます。その際、

  1. ImageVIewの「User Interaction Enabled」にチェックマークをつけましょう。
  2. Content Modeを「Aspect Fit」に変更。
  3. 「Tap Gesture Recognizer」をImageVIew上に設置。(設置すると下の画像の青い四角のアイコンが追加されます。)

スクリーンショット 2018-08-05 21.22.56.png

コード

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: [String : Any]) {

        let selectedImage = info[UIImagePickerControllerOriginalImage] 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 = UIViewContentMode.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:UIImagePickerControllerSourceType =
            UIImagePickerControllerSourceType.photoLibrary

        if UIImagePickerController.isSourceTypeAvailable(
            UIImagePickerControllerSourceType.photoLibrary){
            // インスタンスの作成
            let cameraPicker = UIImagePickerController()
            cameraPicker.sourceType = sourceType
            cameraPicker.delegate = self
            self.present(cameraPicker, animated: true, completion: nil)
        }
        else{
            print("error")

        }
    }

    func tappedcamera() {
        let sourceType:UIImagePickerControllerSourceType =
            UIImagePickerControllerSourceType.camera
        // カメラが利用可能かチェック
        if UIImagePickerController.isSourceTypeAvailable(
            UIImagePickerControllerSourceType.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: UIAlertControllerStyle.actionSheet)

        let tappedcamera = UIAlertAction(title: "カメラで撮影する", style: UIAlertActionStyle.default, handler: {
            (action: UIAlertAction!) in
            self.tappedcamera()
        })

        let tappedlibrary = UIAlertAction(title: "ライブラリから選択する", style: UIAlertActionStyle.default, handler: {
            (action: UIAlertAction!) in
            self.tappedlibrary()
        })

        let cancel = UIAlertAction(title: "キャンセル", style: UIAlertActionStyle.cancel, handler: {
            (action: UIAlertAction!) in
            print("キャンセル")
        })

        actionSheet.addAction(tappedcamera)
        actionSheet.addAction(tappedlibrary)
        actionSheet.addAction(cancel)

        present(actionSheet, animated: true, completion: nil)
    }

}

注意点

今回、画像の縦横比を元の画像に合わせつつ、画像とImageVeiwの大きさが常に一致するようにしている。
理由はタップした座標を取得する際に、ImageViewとImageで座標の原点が変わってしまうからだ。

図のような場合を想定する。
灰色がImageViewで、ライトグリーンがImageだとすると、
星の位置をタップした際に、ImageViewとImageそれぞれ緑色と紫色の矢印のように座標を特定する。

つまり、2つの左上の座標がぴったりあっていないと異なる座標が取得され、意図した場所のRGB値が取れなくなってしまう。

iPhone 8.png

関連付け

普通に関連付けすれば大丈夫ですが、1つだけ注意点があります。
ImageViewをタップした時に動作する

 @IBAction func getImageRGB(_ sender: UITapGestureRecognizer)

ImageViewと繋がずに、「Tap Gesture Recognizer」と繋いでください!(画像の青色の四角)

# GitHub

https://github.com/freadteam/RGBFromImageViewSample