【Swift】明るさとコントラストをCIFilterで調整するアプリを作ってみた

f:id:clrmemory:20170422185647p:plain 

今回はCocoaProgrammingで、CIFilterを使って画像の「明るさとコントラスト」を調整できるOSXアプリを作成しました。

CIFilterを使うことで、ユーザーが調整した値を元に、画像を加工することができます。

 

はじめに

 

今回紹介する方法は「CocoaProgramming」で、使用した言語は「Swift」です。また、Xcodeを使って開発しているため、その他の言語やツールでは正しく機能しないので注意してください。

 

ではまず、作成したOSXアプリの実際の動作を確認しましょう。

 

このように、設定した画像の「明るさ」と「コントラスト」を加工できるようになりました。

SwiftのCIFilterを使うことで画像を加工できるので順に見ていきましょう。はじめにストーリーボードを作成します。

 

ストーリーボードを作成

 

今回作成したOSXアプリでは「NSSlider」を使って「明るさとコントラスト」をユーザーが調整できるようにしました。

 

 

透明でわかりづらいですが、四角い枠の中には「ImageView」、コントラストとブライトネス(明るさ)するためのオブジェクトにはNSSliderをそれぞれ設置しました。

また、明るさとコントラストオブジェクトには「Label」を設置して、どっちがどのオブジェクトか分かりやすくしておきました。

 

今回設置した「NSSlider」の値は

Contrast    -> 0〜2

Brightness -> -1〜1

のように設定しました。

 

また、これらのオブジェクトの「Current」はそれぞれの半分にしましょう。

Contrast = 1, Brightness = 0

 

 

また、これらのスライダーのidentifierには、それぞれ「Contrast」「Brightness」を設定しておいてください。後ほど条件分岐で使用します。

 

これで、NSSliderの初期値を設定できましたね。

さらに、NSSliderの値が変わったらリアルタイムに更新するように設定しましょう。こちらを参考にしてストーリーボードのcontinuousを変更してください。

 

https://clrmemory.com/swift/cocoa-nsslider-change-label/

 

コードの流れ

 

今回は以下のような流れでOSXアプリを作成しました。

 

・画像を設定

・アクションが発生したスライダーを判定

・コントラストの値を設定

・ブライトネス(明るさ)の値を設定

・フィルタを作成

・フィルタをかける

・imageViewを更新

 

画像を設定

 

まずはOutlet接続した「imageView」に画像を設定しましょう。定数「image」の中にNSImageで画像を指定します。

 

let image = NSImage(named: "FilterImage.png")

 

ここで、指定した画像はXcodeのプロジェクト上に設置する必要があります。Xcodeに画像をドラッグすると表示されるウィンドウで以下のように設定しましょう。

 

 

スライダーを判定

 

明るさとコントラストでフィルタをかける場合、どちらのスライダーが操作されたかを判定する必要があります。

 

まずは、NSSliderをアクション接続します。今回はそれぞれのスライダーをまとめて接続しましたが、それについてはこちらで紹介しているので確認してください。

 

https://clrmemory.com/swift/cocoa-package-ibaction/

 

今回の場合、操作されたNSSliderの「Identifier」で判定しました。

 

switch sender.identifier!{...}

 

このswitch文の中で「case “AのIdentifier” :」というような記述をすれば、どちらのNSSliderが操作されたかを判定できました。NSButtonなどでも同様の処理ができるので覚えておきましょう。

 

コントラストの値を設定

 

CIFilterで画像のコントラストを加工する場合のデフォルト値は「1.0」です。そこで今回はスライダーのデフォルトを「1.0」にし、マイナス値が「0.0」プラス値が「2.0」になるようにしました。

 

こちらはストーリーボードで設定したので確認してください。

 

 

contrastValue = sender.floatValue

 

これで、contrastValueの値にスライダーの値(Float型)で設定できました。この値は後ほど作成するフィルタで設定します。

 

明るさの値を設定

 

CIFilterで設定する明るさは「brightnessValue」で設定しました。コントラストで設定した時と同様にNSSliderのアクションで設定します。

 

デフォルト値は「0.0」、マイナス値は「-1.0」プラス値は「2.0」にしました。こちらもストーリーボードから設定しているので、先に確認しておきましょう。

 

 

最後にcontrastValueと同様「brightnessValue」にスライダーの値を格納するだけですね。

 

brightnessValue = sender.floatValue

 

こうして完成したNSSliderのアクション処理がこちら

@IBAction func sliderAction(_ sender: NSSlider) {

    switch sender.identifier!{
    case "Contrast":
        contrastValue = sender.floatValue
    case "Brightness":
        brightnessValue = sender.floatValue
    default: break
    }
    print("\(contrastValue), \(brightnessValue)")
    imageFilter()
}

 

printの処理は不要です。また、imageFilter( )はこの後作成するCIFilterで定義するので、現段階ではエラーになります。

 

フィルタを作成

 

続いて画像にかける「フィルタ」を作成していきましょう。先日の記事でも紹介したのですが、CIFilterでは画像をデータとして取得し、フィルタを追加してからNSImageで取得し直すといった処理をします。

 

func imageFilter(){
    let ciImage = CIImage(data: image!.tiffRepresentation!)
    let filter  = CIFilter(name: "CIColorControls")!
    filter.setValue(ciImage, forKey: kCIInputImageKey)
    filter.setValue(contrastValue, forKey: kCIInputContrastKey)
    filter.setValue(brightnessValue, forKey: kCIInputBrightnessKey)
    let ciImageFilter = filter.outputImage!
    let nsImage = NSImage(size: ciImageFilter.extent.size)

    nsImage.addRepresentation(NSCIImageRep(ciImage: ciImageFilter))
    imageView.image = nsImage

}

 

tiffRepresentationでデータ変換

・フィルタ名を設定し、値とキーを設定

・CIImageをNSImageで取得

・再描画

 

という流れになっており、明るさやコントラストを設定するためのフィルタ名は「"CIColorControls"」です。

またコントラストのキーは「kCIInputContrastKey」

ブライトネス(明るさ)のキーは「kCIInputBrightnessKey」

で設定してください。

 

これで作成できたフィルタを元に、NSImageとして画像を取得してimageViewに反映することで、画像にフィルタをかけることができたかと思います。

 

試しに画像を明るく加工してみました。

 

デフォルト画像:

f:id:clrmemory:20170514130752p:plain

 

アプリ内で加工した画像:

 

追記:彩度のフィルタを追加

 

こちらの記事で、彩度にもフィルタをかけられるようにしました。

 

[https://www.clrmemory.com/swift/cocoa-saturation-cifilter/:embed:cite]

 

また、今回紹介したもの以外にもCIFilterには様々なフィルタがあります。これまで紹介した記事をまとめてあるので参考にしてみてください。

 

CIFilterまとめ

 

 

完成したコード

 

最後にOutlet接続やAction接続、変数などの定義を含めた全コードを確認しましょう。

 

import Cocoa

class ViewController: NSViewController {

    @IBOutlet weak var imageView: NSImageView!
    let image = NSImage(named: "FilterImage.png")
    var contrastValue: Float = 1.0
    var brightnessValue: Float = 0.0

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        imageView.image = image
    }
    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }
    func imageFilter(){
        let ciImage = CIImage(data: image!.tiffRepresentation!)
        let filter  = CIFilter(name: "CIColorControls")!
        filter.setValue(ciImage, forKey: kCIInputImageKey)
        filter.setValue(contrastValue, forKey: kCIInputContrastKey)
        filter.setValue(brightnessValue, forKey: kCIInputBrightnessKey)
        let ciImageFilter = filter.outputImage!
        let nsImage = NSImage(size: ciImageFilter.extent.size)

        nsImage.addRepresentation(NSCIImageRep(ciImage: ciImageFilter))
        imageView.image = nsImage

    }

    @IBAction func sliderAction(_ sender: NSSlider) {

        switch sender.identifier!{
        case "Contrast":
            contrastValue = sender.floatValue
        case "Brightness":
            brightnessValue = sender.floatValue
        default: break
        }
        print("\(contrastValue), \(brightnessValue)")
        imageFilter()
    }
}

 

これで明るさとコントラストを調整できるCocoaアプリが完成しました。

 

まとめ

 

今回紹介したように、CIFilterを使うことで簡単に画像を調整できるOSXアプリが完成しました。CIFilterは似たような記述で様々なフィルタをかけることができます。

 

今回はひとまず

・CIColorControls

・kCIInputContrastKey

・kCIInputBrightnessKey

あたりを覚えておきましょう。

 

ではまた。

新着記事