├── ios.png
├── mac.png
├── DominantColor
├── .gitignore
├── Mac
│ ├── ExampleMac-Bridging-Header.h
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── DragAndDropImageView.swift
│ ├── AppDelegate.swift
│ └── Base.lproj
│ │ └── MainMenu.xib
├── iOS
│ ├── ExampleiOS-Bridging-Header.h
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── AppDelegate.swift
│ ├── ViewController.swift
│ └── Base.lproj
│ │ ├── LaunchScreen.xib
│ │ └── Main.storyboard
└── Shared
│ ├── ColorSpaceConversion.h
│ ├── INVector3.m
│ ├── INVector3.h
│ ├── Memoization.swift
│ ├── DominantColor.h
│ ├── INVector3SwiftExtensions.swift
│ ├── Info.plist
│ ├── KMeans.swift
│ ├── ColorDifference.swift
│ ├── PlatformExtensions.swift
│ ├── ColorSpaceConversion.m
│ └── DominantColors.swift
├── DominantColor.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcshareddata
│ └── xcschemes
│ │ ├── DominantColor-Mac.xcscheme
│ │ ├── DominantColor-iOS.xcscheme
│ │ ├── ExampleMac.xcscheme
│ │ └── ExampleiOS.xcscheme
└── project.pbxproj
├── .gitignore
├── DominantColor.podspec
├── LICENSE
└── README.md
/ios.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objcio/DominantColor/HEAD/ios.png
--------------------------------------------------------------------------------
/mac.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/objcio/DominantColor/HEAD/mac.png
--------------------------------------------------------------------------------
/DominantColor/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.so
3 | _test
4 | *.swp
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/DominantColor/Mac/ExampleMac-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #include
6 | extern uint64_t dispatch_benchmark(size_t count, void (^block)(void));
7 |
--------------------------------------------------------------------------------
/DominantColor/iOS/ExampleiOS-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #include
6 | extern uint64_t dispatch_benchmark(size_t count, void (^block)(void));
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
--------------------------------------------------------------------------------
/DominantColor/Shared/ColorSpaceConversion.h:
--------------------------------------------------------------------------------
1 | //
2 | // ColorSpaceConversion.h
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/21/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #import "INVector3.h"
10 |
11 | // Simple RGB <-> LAB conversion functions assuming a D65 illuminant
12 | // with the standard 2° observer for CIE 1931.
13 |
14 | INVector3 IN_RGBToLAB(INVector3 rgbVector);
15 | INVector3 IN_LABToRGB(INVector3 labVector);
16 |
--------------------------------------------------------------------------------
/DominantColor/Shared/INVector3.m:
--------------------------------------------------------------------------------
1 | //
2 | // INVector3.c
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/21/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #import "INVector3.h"
10 |
11 | GLKVector3 INVector3ToGLKVector3(INVector3 vector) {
12 | return GLKVector3Make(vector.x, vector.y, vector.z);
13 | }
14 |
15 | INVector3 GLKVector3ToINVector3(GLKVector3 vector) {
16 | return (INVector3){ vector.x, vector.y, vector.z };
17 | }
18 |
--------------------------------------------------------------------------------
/DominantColor/Shared/INVector3.h:
--------------------------------------------------------------------------------
1 | //
2 | // INVector3.h
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/21/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | // Wrapping GLKVector3 values in a struct so that it can be used from Swift.
12 |
13 | typedef struct {
14 | float x;
15 | float y;
16 | float z;
17 | } INVector3;
18 |
19 | GLKVector3 INVector3ToGLKVector3(INVector3 vector);
20 | INVector3 GLKVector3ToINVector3(GLKVector3 vector);
21 |
--------------------------------------------------------------------------------
/DominantColor/Shared/Memoization.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Memoization.swift
3 | // DominantColor
4 | //
5 | // Created by Emmanuel Odeke on 2014-12-25.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | func memoize(f: T -> U) -> T -> U {
10 | var cache = [T : U]()
11 |
12 | return { key in
13 | var value = cache[key]
14 | if value == nil {
15 | value = f(key)
16 | cache[key] = value
17 | }
18 | return value!
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/DominantColor/Shared/DominantColor.h:
--------------------------------------------------------------------------------
1 | //
2 | // DominantColor.h
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/22/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for DominantColor.
12 | FOUNDATION_EXPORT double DominantColorVersionNumber;
13 |
14 | //! Project version string for DominantColor.
15 | FOUNDATION_EXPORT const unsigned char DominantColorVersionString[];
16 |
17 | #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/DominantColor.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = 'DominantColor'
3 | spec.version = '0.1.0'
4 | spec.summary = 'Finding dominant colors of an image using k-means clustering.'
5 | spec.homepage = 'https://github.com/indragiek/DominantColor'
6 | spec.license = 'MIT'
7 | spec.author = { 'Indragie Karunaratne' => 'i@indragie.com' }
8 | spec.source = { :git => 'https://github.com/indragiek/DominantColor.git', :tag => spec.version.to_s }
9 | spec.source_files = 'DominantColor/Shared/*.{swift,h,m}'
10 | spec.requires_arc = true
11 | spec.frameworks = 'GLKit'
12 | spec.ios.deployment_target = '8.0'
13 | spec.osx.deployment_target = '10.9'
14 | spec.ios.frameworks = 'UIKit'
15 | spec.osx.frameworks = 'Cocoa'
16 | end
--------------------------------------------------------------------------------
/DominantColor/iOS/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/DominantColor/Shared/INVector3SwiftExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // INVector3SwiftExtensions.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/24/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | extension INVector3 {
10 | func unpack() -> (Float, Float, Float) {
11 | return (x, y, z)
12 | }
13 |
14 | static var identity: INVector3 {
15 | return INVector3(x: 0, y: 0, z: 0)
16 | }
17 | }
18 |
19 | func +(lhs: INVector3, rhs: INVector3) -> INVector3 {
20 | return INVector3(x: lhs.x + rhs.x, y: lhs.y + rhs.y, z: lhs.z + rhs.z)
21 | }
22 |
23 | func /(lhs: INVector3, rhs: Float) -> INVector3 {
24 | return INVector3(x: lhs.x / rhs, y: lhs.y / rhs, z: lhs.z / rhs)
25 | }
26 |
27 | func /(lhs: INVector3, rhs: Int) -> INVector3 {
28 | return lhs / Float(rhs)
29 | }
30 |
--------------------------------------------------------------------------------
/DominantColor/Shared/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.indragie.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSHumanReadableCopyright
24 | Copyright © 2014 Indragie Karunaratne. All rights reserved.
25 | NSPrincipalClass
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Indragie Karunaratne
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/DominantColor/Mac/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/DominantColor/Mac/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | com.indragie.$(PRODUCT_NAME:rfc1034identifier)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | Copyright © 2014 Indragie Karunaratne. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/DominantColor/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.jamalDesigns.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/DominantColor/Mac/DragAndDropImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DragAndDropImageView.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/19/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @objc protocol DragAndDropImageViewDelegate {
12 | func dragAndDropImageView(imageView: DragAndDropImageView, droppedImage image: NSImage?)
13 | }
14 |
15 | class DragAndDropImageView: NSImageView {
16 | @IBOutlet weak var delegate: DragAndDropImageViewDelegate?
17 |
18 | required init?(coder: NSCoder) {
19 | super.init(coder: coder)
20 | registerForDraggedTypes([NSFilenamesPboardType, NSTIFFPboardType])
21 | }
22 |
23 | // MARK: NSDraggingDestination
24 |
25 | override func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
26 | let pasteboard = sender.draggingPasteboard()
27 | if let data = pasteboard.dataForType(NSTIFFPboardType) {
28 | return .Copy
29 | } else if let files = pasteboard.propertyListForType(NSFilenamesPboardType) as? [String] {
30 | let UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, files[0].pathExtension, nil).takeRetainedValue()
31 | return (UTTypeConformsTo(UTI, kUTTypeImage) == 1) ? .Copy : .None
32 | }
33 | return .None
34 | }
35 |
36 | override func performDragOperation(sender: NSDraggingInfo) -> Bool {
37 | let pasteboard = sender.draggingPasteboard()
38 | if let data = pasteboard.dataForType(NSTIFFPboardType) {
39 | self.delegate?.dragAndDropImageView(self, droppedImage: NSImage(data: data))
40 | } else if let files = pasteboard.propertyListForType(NSFilenamesPboardType) as? [String] {
41 | self.delegate?.dragAndDropImageView(self, droppedImage: NSImage(contentsOfFile: files[0]))
42 | }
43 | return true
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/DominantColor/Mac/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/18/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import DominantColor
11 |
12 | @NSApplicationMain
13 | class AppDelegate: NSObject, NSApplicationDelegate, DragAndDropImageViewDelegate {
14 |
15 | @IBOutlet weak var window: NSWindow!
16 | @IBOutlet weak var box1: NSBox!
17 | @IBOutlet weak var box2: NSBox!
18 | @IBOutlet weak var box3: NSBox!
19 | @IBOutlet weak var box4: NSBox!
20 | @IBOutlet weak var box5: NSBox!
21 | @IBOutlet weak var box6: NSBox!
22 | @IBOutlet weak var imageView: DragAndDropImageView!
23 |
24 | var image: NSImage?
25 |
26 | func applicationDidFinishLaunching(aNotification: NSNotification) {
27 | imageView.delegate = self
28 | }
29 |
30 | // MARK: DragAndDropImageViewDelegate
31 |
32 | @IBAction func runBenchmark(sender: NSButton) {
33 | if let image = image {
34 | let nValues: [Int] = [100, 1000, 2000, 5000, 10000]
35 | let CGImage = image.CGImageForProposedRect(nil, context: nil, hints: nil)!.takeUnretainedValue()
36 | for n in nValues {
37 | let ns = dispatch_benchmark(5) {
38 | dominantColorsInImage(CGImage, maxSampledPixels: n)
39 | return
40 | }
41 | println("n = \(n) averaged \(ns/1000000) ms")
42 | }
43 | }
44 | }
45 |
46 | func dragAndDropImageView(imageView: DragAndDropImageView, droppedImage image: NSImage?) {
47 | if let image = image {
48 | imageView.image = image
49 |
50 | self.image = image
51 | let colors = image.dominantColors()
52 | let boxes = [box1, box2, box3, box4, box5, box6]
53 |
54 | for box in boxes {
55 | box.fillColor = NSColor.clearColor()
56 | }
57 | for i in 0.. Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(application: UIApplication) {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/DominantColor/iOS/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Dominant Color iOS
4 | //
5 | // Created by Jamal E. Kharrat on 12/22/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import DominantColor
11 |
12 | class ViewController: UIViewController , UIImagePickerControllerDelegate, UINavigationControllerDelegate {
13 |
14 | @IBOutlet var boxes: [UIView]!
15 | @IBOutlet weak var imageView: UIImageView!
16 |
17 | var image: UIImage!
18 |
19 | // MARK: IBActions
20 |
21 | @IBAction func selectTapped(sender: AnyObject) {
22 | let imagePicker = UIImagePickerController()
23 | imagePicker.delegate = self
24 | imagePicker.sourceType = .PhotoLibrary
25 | self.presentViewController(imagePicker, animated: true, completion: nil)
26 | }
27 |
28 | @IBAction func runBenchmarkTapped(sender: AnyObject) {
29 | if let image = image {
30 | let nValues: [Int] = [100, 1000, 2000, 5000, 10000]
31 | let CGImage = image.CGImage
32 | for n in nValues {
33 | let ns = dispatch_benchmark(5) {
34 | dominantColorsInImage(CGImage, maxSampledPixels: n)
35 | return
36 | }
37 | println("n = \(n) averaged \(ns/1000000) ms")
38 | }
39 | }
40 | }
41 |
42 | // MARK: ImagePicker Delegate
43 |
44 | func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage!, editingInfo: [NSObject : AnyObject]!) {
45 | if let imageSelected = image {
46 | self.image = imageSelected
47 | imageView.image = imageSelected
48 |
49 | let colors = imageSelected.dominantColors()
50 | for box in boxes {
51 | box.backgroundColor = UIColor.clearColor()
52 | }
53 | for i in 0..
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
60 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/xcshareddata/xcschemes/DominantColor-iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
60 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/xcshareddata/xcschemes/ExampleMac.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
69 |
70 |
76 |
77 |
78 |
79 |
81 |
82 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/xcshareddata/xcschemes/ExampleiOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
51 |
52 |
58 |
59 |
60 |
61 |
62 |
63 |
69 |
70 |
76 |
77 |
78 |
79 |
81 |
82 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/DominantColor/iOS/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/DominantColor/Shared/KMeans.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KMeans.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/20/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | import Darwin
10 |
11 | // Represents a type that can be clustered using the k-means clustering
12 | // algorithm.
13 | protocol ClusteredType {
14 | // Used to compute average values to determine the cluster centroids.
15 | func +(lhs: Self, rhs: Self) -> Self
16 | func /(lhs: Self, rhs: Int) -> Self
17 |
18 | // Identity value such that x + identity = x. Typically the 0 vector.
19 | static var identity: Self { get }
20 | }
21 |
22 | struct Cluster {
23 | let centroid: T
24 | let size: Int
25 | }
26 |
27 | // k-means clustering algorithm from
28 | // http://users.eecs.northwestern.edu/~wkliao/Kmeans/
29 |
30 | func kmeans(
31 | points: [T],
32 | k: Int,
33 | seed: UInt32,
34 | distance: ((T, T) -> Float),
35 | threshold: Float = 0.0001
36 | ) -> [Cluster] {
37 |
38 | let n = count(points)
39 | assert(k <= n, "k cannot be larger than the total number of points")
40 |
41 | var centroids = points.randomValues(seed, num: k)
42 | var memberships = [Int](count: n, repeatedValue: -1)
43 | var clusterSizes = [Int](count: k, repeatedValue: 0)
44 |
45 | var error: Float = 0
46 | var previousError: Float = 0
47 |
48 | do {
49 | error = 0
50 | var newCentroids = [T](count: k, repeatedValue: T.identity)
51 | var newClusterSizes = [Int](count: k, repeatedValue: 0)
52 |
53 | for i in 0.. 0 {
66 | centroids[i] = newCentroids[i] / size
67 | }
68 | }
69 |
70 | clusterSizes = newClusterSizes
71 | previousError = error
72 | } while abs(error - previousError) > threshold
73 |
74 | return map(Zip2(centroids, clusterSizes)) { Cluster(centroid: $0, size: $1) }
75 | }
76 |
77 | private func findNearestCluster(point: T, centroids: [T], k: Int, distance: (T, T) -> Float) -> Int {
78 | var minDistance = Float.infinity
79 | var clusterIndex = 0
80 | for i in 0..) -> Int {
91 | let interval = range.endIndex - range.startIndex - 1
92 | let buckets = Int(RAND_MAX) / interval
93 | let limit = buckets * interval
94 | var r = 0
95 | do {
96 | r = Int(rand())
97 | } while r >= limit
98 | return range.startIndex + (r / buckets)
99 | }
100 |
101 | private extension Array {
102 | private func randomValues(seed: UInt32, num: Int) -> [T] {
103 | srand(seed)
104 |
105 | var indices = [Int]()
106 | indices.reserveCapacity(num)
107 | let range = 0.. Float {
16 | let (L1, a1, b1) = lab1.unpack()
17 | let (L2, a2, b2) = lab2.unpack()
18 |
19 | return pow(L2 - L1, 2) + pow(a2 - a1, 2) + pow(b2 - b1, 2)
20 | }
21 |
22 | private func C(a: Float, b: Float) -> Float {
23 | return sqrt(pow(a, 2) + pow(b, 2))
24 | }
25 |
26 | // From http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE94.html
27 | func CIE94SquaredColorDifference(
28 | kL: Float = 1,
29 | kC: Float = 1,
30 | kH: Float = 1,
31 | K1: Float = 0.045,
32 | K2: Float = 0.015
33 | )(lab1:INVector3, lab2:INVector3) -> Float {
34 |
35 | let (L1, a1, b1) = lab1.unpack()
36 | let (L2, a2, b2) = lab2.unpack()
37 | let ΔL = L1 - L2
38 |
39 | let (C1, C2) = (C(a1, b1), C(a2, b2))
40 | let ΔC = C1 - C2
41 |
42 | let ΔH = sqrt(pow(a1 - a2, 2) + pow(b1 - b2, 2) - pow(ΔC, 2))
43 |
44 | let Sl: Float = 1
45 | let Sc = 1 + K1 * C1
46 | let Sh = 1 + K2 * C1
47 |
48 | return pow(ΔL / (kL * Sl), 2) + pow(ΔC / (kC * Sc), 2) + pow(ΔH / (kH * Sh), 2)
49 | }
50 |
51 | // From http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE2000.html
52 | func CIE2000SquaredColorDifference(
53 | kL: Float = 1,
54 | kC: Float = 1,
55 | kH: Float = 1
56 | )(lab1:INVector3, lab2:INVector3) -> Float {
57 |
58 | let (L1, a1, b1) = lab1.unpack()
59 | let (L2, a2, b2) = lab2.unpack()
60 |
61 | let ΔLp = L2 - L1
62 | let Lbp = (L1 + L2) / 2
63 |
64 | let (C1, C2) = (C(a1, b1), C(a2, b2))
65 | let Cb = (C1 + C2) / 2
66 |
67 | let G = (1 - sqrt(pow(Cb, 7) / (pow(Cb, 7) + pow(25, 7)))) / 2
68 | let ap: Float -> Float = { a in
69 | return a * (1 + G)
70 | }
71 | let (a1p, a2p) = (ap(a1), ap(a2))
72 |
73 | let (C1p, C2p) = (C(a1p, b1), C(a2p, b2))
74 | let ΔCp = C2p - C1p
75 | let Cbp = (C1p + C2p) / 2
76 |
77 | let hp: (Float, Float) -> Float = { ap, b in
78 | if ap == 0 && b == 0 { return 0 }
79 | let θ = GLKMathRadiansToDegrees(atan2(b, ap))
80 | return fmod(θ < 0 ? (θ + 360) : θ, 360)
81 | }
82 | let (h1p, h2p) = (hp(a1p, b1), hp(a2p, b2))
83 | let Δhabs = abs(h1p - h2p)
84 | let Δhp: Float = {
85 | if (C1p == 0 || C2p == 0) {
86 | return 0
87 | } else if Δhabs <= 180 {
88 | return h2p - h1p
89 | } else if h2p <= h1p {
90 | return h2p - h1p + 360
91 | } else {
92 | return h2p - h1p - 360
93 | }
94 | }()
95 |
96 | let ΔHp = 2 * sqrt(C1p * C2p) * sin(GLKMathDegreesToRadians(Δhp / 2))
97 | let Hbp: Float = {
98 | if (C1p == 0 || C2p == 0) {
99 | return h1p + h2p
100 | } else if Δhabs > 180 {
101 | return (h1p + h2p + 360) / 2
102 | } else {
103 | return (h1p + h2p) / 2
104 | }
105 | }()
106 |
107 | let T = 1
108 | - 0.17 * cos(GLKMathDegreesToRadians(Hbp - 30))
109 | + 0.24 * cos(GLKMathDegreesToRadians(2 * Hbp))
110 | + 0.32 * cos(GLKMathDegreesToRadians(3 * Hbp + 6))
111 | - 0.20 * cos(GLKMathDegreesToRadians(4 * Hbp - 63))
112 |
113 | let Sl = 1 + (0.015 * pow(Lbp - 50, 2)) / sqrt(20 + pow(Lbp - 50, 2))
114 | let Sc = 1 + 0.045 * Cbp
115 | let Sh = 1 + 0.015 * Cbp * T
116 |
117 | let Δθ = 30 * exp(-pow((Hbp - 275) / 25, 2))
118 | let Rc = 2 * sqrt(pow(Cbp, 7) / (pow(Cbp, 7) + pow(25, 7)))
119 | let Rt = -Rc * sin(GLKMathDegreesToRadians(2 * Δθ))
120 |
121 | let Lterm = ΔLp / (kL * Sl)
122 | let Cterm = ΔCp / (kC * Sc)
123 | let Hterm = ΔHp / (kH * Sh)
124 | return pow(Lterm, 2) + pow(Cterm, 2) + pow(Hterm, 2) + Rt * Cterm * Hterm
125 | }
126 |
--------------------------------------------------------------------------------
/DominantColor/Shared/PlatformExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PlatformExtensions.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/25/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #if os(OSX)
10 | import Cocoa
11 |
12 | public extension NSImage {
13 | /**
14 | Computes the dominant colors in the receiver
15 |
16 | :param: maxSampledPixels Maximum number of pixels to sample in the image. If
17 | the total number of pixels in the image exceeds this
18 | value, it will be downsampled to meet the constraint.
19 | :param: accuracy Level of accuracy to use when grouping similar colors.
20 | Higher accuracy will come with a performance tradeoff.
21 | :param: seed Seed to use when choosing the initial points for grouping
22 | of similar colors. The same seed is guaranteed to return
23 | the same colors every time.
24 | :param: memoizeConversions Whether to memoize conversions from RGB to the LAB color
25 | space (used for grouping similar colors). Memoization
26 | will only yield better performance for large values of
27 | `maxSampledPixels` in images that are primarily comprised
28 | of flat colors. If this information about the image is
29 | not known beforehand, it is best to not memoize.
30 |
31 | :returns: A list of dominant colors in the image sorted from most dominant to
32 | least dominant.
33 | */
34 | public func dominantColors(
35 | maxSampledPixels: Int = DefaultParameterValues.maxSampledPixels,
36 | accuracy: GroupingAccuracy = DefaultParameterValues.accuracy,
37 | seed: UInt32 = DefaultParameterValues.seed,
38 | memoizeConversions: Bool = DefaultParameterValues.memoizeConversions
39 | ) -> [NSColor] {
40 | let image = CGImageForProposedRect(nil, context: nil, hints: nil)!.takeUnretainedValue()
41 | let colors = dominantColorsInImage(image, maxSampledPixels: maxSampledPixels, accuracy: accuracy, seed: seed, memoizeConversions: memoizeConversions)
42 | return colors.map { NSColor(CGColor: $0)! }
43 | }
44 | }
45 |
46 | #elseif os(iOS)
47 | import UIKit
48 |
49 | public extension UIImage {
50 | /**
51 | Computes the dominant colors in the receiver
52 |
53 | :param: maxSampledPixels Maximum number of pixels to sample in the image. If
54 | the total number of pixels in the image exceeds this
55 | value, it will be downsampled to meet the constraint.
56 | :param: accuracy Level of accuracy to use when grouping similar colors.
57 | Higher accuracy will come with a performance tradeoff.
58 | :param: seed Seed to use when choosing the initial points for grouping
59 | of similar colors. The same seed is guaranteed to return
60 | the same colors every time.
61 | :param: memoizeConversions Whether to memoize conversions from RGB to the LAB color
62 | space (used for grouping similar colors). Memoization
63 | will only yield better performance for large values of
64 | `maxSampledPixels` in images that are primarily comprised
65 | of flat colors. If this information about the image is
66 | not known beforehand, it is best to not memoize.
67 |
68 | :returns: A list of dominant colors in the image sorted from most dominant to
69 | least dominant.
70 | */
71 | public func dominantColors(
72 | maxSampledPixels: Int = DefaultParameterValues.maxSampledPixels,
73 | accuracy: GroupingAccuracy = DefaultParameterValues.accuracy,
74 | seed: UInt32 = DefaultParameterValues.seed,
75 | memoizeConversions: Bool = DefaultParameterValues.memoizeConversions
76 | ) -> [UIColor] {
77 | let colors = dominantColorsInImage(self.CGImage, maxSampledPixels: maxSampledPixels, accuracy: accuracy, seed: seed, memoizeConversions: memoizeConversions)
78 | return colors.map { UIColor(CGColor: $0)! }
79 | }
80 | }
81 |
82 | #endif
83 |
84 |
--------------------------------------------------------------------------------
/DominantColor/Shared/ColorSpaceConversion.m:
--------------------------------------------------------------------------------
1 | //
2 | // ColorSpaceConversion.m
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/21/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #import "ColorSpaceConversion.h"
10 |
11 | #pragma mark - RGB
12 |
13 | static GLKVector3 RGBToSRGB(GLKVector3 rgbVector) {
14 | #if TARGET_OS_IPHONE
15 | // sRGB is the native device color space on iOS, no conversion is required.
16 | return rgbVector;
17 | #else
18 | NSColor *rgbColor = [NSColor colorWithDeviceRed:rgbVector.x green:rgbVector.y blue:rgbVector.z alpha:1.0];
19 | NSColor *srgbColor = [rgbColor colorUsingColorSpace:NSColorSpace.sRGBColorSpace];
20 | return GLKVector3Make(srgbColor.redComponent, srgbColor.greenComponent, srgbColor.blueComponent);
21 | #endif
22 | }
23 |
24 | static GLKVector3 SRGBToRGB(GLKVector3 srgbVector) {
25 | #if TARGET_OS_IPHONE
26 | // sRGB is the native device color space on iOS, no conversion is required.
27 | return srgbVector;
28 | #else
29 | const CGFloat components[4] = { srgbVector.x, srgbVector.y, srgbVector.z, 1.0 };
30 | NSColor *srgbColor = [NSColor colorWithColorSpace:NSColorSpace.sRGBColorSpace components:components count:4];
31 | NSColor *rgbColor = [srgbColor colorUsingColorSpace:NSColorSpace.deviceRGBColorSpace];
32 | return GLKVector3Make(rgbColor.redComponent, rgbColor.greenComponent, rgbColor.blueComponent);
33 | #endif
34 | }
35 |
36 | #pragma mark - SRGB
37 | // http://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation
38 |
39 | static GLKVector3 SRGBToLinearSRGB(GLKVector3 srgbVector) {
40 | float (^f)(float) = ^float (float c) {
41 | if (c <= 0.04045f) {
42 | return c / 12.92f;
43 | } else {
44 | return powf((c + 0.055f) / 1.055f, 2.4f);
45 | }
46 | };
47 | return GLKVector3Make(f(srgbVector.x), f(srgbVector.y), f(srgbVector.z));
48 | }
49 |
50 | static GLKVector3 LinearSRGBToSRGB(GLKVector3 lSrgbVector) {
51 | float (^f)(float) = ^float (float c) {
52 | if (c <= 0.0031308f) {
53 | return c * 12.92f;
54 | } else {
55 | return (1.055f * powf(c, 1.f / 2.4f)) - 0.055f;
56 | }
57 | };
58 | return GLKVector3Make(f(lSrgbVector.x), f(lSrgbVector.y), f(lSrgbVector.z));
59 | }
60 |
61 | #pragma mark - XYZ (CIE 1931)
62 | // http://en.wikipedia.org/wiki/CIE_1931_color_space#Construction_of_the_CIE_XYZ_color_space_from_the_Wright.E2.80.93Guild_data
63 |
64 | static const GLKMatrix3 LinearSRGBToXYZMatrix = (GLKMatrix3){
65 | 0.4124f, 0.2126f, 0.0193f,
66 | 0.3576f, 0.7152f, 0.1192f,
67 | 0.1805f, 0.0722f, 0.9505f
68 | };
69 |
70 | static GLKVector3 LinearSRGBToXYZ(GLKVector3 linearSrgbVector) {
71 | const GLKVector3 unscaledXYZVector = GLKMatrix3MultiplyVector3(LinearSRGBToXYZMatrix, linearSrgbVector);
72 | return GLKVector3MultiplyScalar(unscaledXYZVector, 100.f);
73 | }
74 |
75 | static const GLKMatrix3 XYZToLinearSRGBMatrix = (GLKMatrix3){
76 | 3.2406f, -0.9689f, 0.0557f,
77 | -1.5372f, 1.8758f, -0.2040f,
78 | -0.4986f, 0.0415f, 1.0570f
79 | };
80 |
81 | static GLKVector3 XYZToLinearSRGB(GLKVector3 xyzVector) {
82 | const GLKVector3 scaledXYZVector = GLKVector3DivideScalar(xyzVector, 100.f);
83 | return GLKMatrix3MultiplyVector3(XYZToLinearSRGBMatrix, scaledXYZVector);
84 | }
85 |
86 | #pragma mark - LAB
87 | // http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions
88 |
89 | static GLKVector3 XYZToLAB(GLKVector3 xyzVector, GLKVector3 tristimulus) {
90 | float (^f)(float) = ^float (float t) {
91 | if (t > powf(6.f / 29.f, 3.f)) {
92 | return powf(t, 1.f / 3.f);
93 | } else {
94 | return ((1.f / 3.f) * powf(29.f / 6.f, 2.f) * t) + (4.f / 29.f);
95 | }
96 | };
97 | const float fx = f(xyzVector.x / tristimulus.x);
98 | const float fy = f(xyzVector.y / tristimulus.y);
99 | const float fz = f(xyzVector.z / tristimulus.z);
100 |
101 | const float l = (116.f * fy) - 16.f;
102 | const float a = 500 * (fx - fy);
103 | const float b = 200 * (fy - fz);
104 |
105 | return GLKVector3Make(l, a, b);
106 | }
107 |
108 | static GLKVector3 LABToXYZ(GLKVector3 labVector, GLKVector3 tristimulus) {
109 | float (^f)(float) = ^float (float t) {
110 | if (t > (6.f / 29.f)) {
111 | return powf(t, 3.f);
112 | } else {
113 | return 3.f * powf(6.f / 29.f, 2.f) * (t - (4.f / 29.f));
114 | }
115 | };
116 | const float c = (1.f / 116.f) * (labVector.x + 16.f);
117 |
118 | const float y = tristimulus.y * f(c);
119 | const float x = tristimulus.x * f(c + ((1.f / 500.f) * labVector.y));
120 | const float z = tristimulus.z * f(c - ((1.f / 200.f) * labVector.z));
121 |
122 | return GLKVector3Make(x, y, z);
123 | }
124 |
125 | #pragma mark - Public
126 |
127 | // From http://www.easyrgb.com/index.php?X=MATH&H=15#text15
128 | static const GLKVector3 D65Tristimulus = (GLKVector3){ 95.047f, 100.f, 108.883f };
129 |
130 | INVector3 IN_RGBToLAB(INVector3 rgbVector) {
131 | const GLKVector3 gVector = INVector3ToGLKVector3(rgbVector);
132 | const GLKVector3 srgbVector = RGBToSRGB(gVector);
133 | const GLKVector3 lSrgbVector = SRGBToLinearSRGB(srgbVector);
134 | const GLKVector3 xyzVector = LinearSRGBToXYZ(lSrgbVector);
135 | const GLKVector3 labVector = XYZToLAB(xyzVector, D65Tristimulus);
136 | return GLKVector3ToINVector3(labVector);
137 | }
138 |
139 | INVector3 IN_LABToRGB(INVector3 labVector) {
140 | const GLKVector3 gVector = INVector3ToGLKVector3(labVector);
141 | const GLKVector3 xyzVector = LABToXYZ(gVector, D65Tristimulus);
142 | const GLKVector3 lSrgbVector = XYZToLinearSRGB(xyzVector);
143 | const GLKVector3 srgbVector = LinearSRGBToSRGB(lSrgbVector);
144 | const GLKVector3 rgbVector = SRGBToRGB(srgbVector);
145 | return GLKVector3ToINVector3(rgbVector);
146 | }
147 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## DominantColor
2 |
3 | Finding the dominant colors of an image using the CIE LAB color space and the k-means clustering algorithm.
4 |
5 | ### Algorithm
6 |
7 | #### Color Space
8 |
9 | The RGB color space does not take human perception into account, so the CIELAB color space is used instead, which is designed to approximate human vision [[1]](http://en.wikipedia.org/wiki/Lab_color_space#Advantages).
10 |
11 | ##### Conversion from RGB
12 |
13 | The conversion from RGB values to LAB values requires first transforming the RGB values to an absolute color space like sRGB[[2]](http://en.wikipedia.org/wiki/Lab_color_space#RGB_and_CMYK_conversions). On iOS, this conversion is not necessary because sRGB is the native device color space[[3]](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGColorSpace/index.html). On OS X, the conversion can be performed using [`-[NSColorSpace sRGBColorSpace]`](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSColorSpace_Class/index.html).
14 |
15 | Once the colors have been converted to sRGB, they are first converted to linear sRGB values and then to CIE XYZ values[[4]](http://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29). Finally, they are converted to the CIE LAB color space[[5]](http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions) using a D65 standard illuminant[[6]](http://en.wikipedia.org/wiki/Illuminant_D65)[[7]](http://www.easyrgb.com/index.php?X=MATH&H=15#text15).
16 |
17 | #### Color Difference
18 |
19 | A color difference algorithm is used to group similar colors. API consumers can choose between the CIE 76[[8]](http://en.wikipedia.org/wiki/Color_difference#CIE76), CIE 94[[9]](http://en.wikipedia.org/wiki/Color_difference#CIE94), and CIE 2000[[10]](http://en.wikipedia.org/wiki/Color_difference#CIEDE2000) algorithms for low, medium, and high color grouping accuracy, respectively. The default algorithm is CIE 94, as it provides results that are close to CIE 2000 with a negligible performance impact in comparison to CIE 76.
20 |
21 | #### Clustering (k-means)
22 |
23 | Pixels are grouped into clusters of dominant colors using a standard k-means clustering algorithm[[11](http://en.wikipedia.org/wiki/K-means_clustering)][[12](http://users.eecs.northwestern.edu/~wkliao/Kmeans/)][[13](http://cs.smu.ca/~r_zhang/code/kmeans.c)].
24 |
25 | ##### Choosing K
26 |
27 | The k-value was originally chosen based on the rule of thumb `k = sqrt(n/2)`[[14](http://en.wikipedia.org/wiki/Determining_the_number_of_clusters_in_a_data_set#cite_note-1)] but this resulted in `k`-values that were too large to run in a reasonable amount of time for large values of `n`. Right now, I'm using a magic value of `16` because empirical testing showed that it yielded the best results for many different images but I'm still looking into a number of more data-driven alternate approaches.
28 |
29 | ##### Selecting Initial Centroids
30 |
31 | The initial centroids are currently selected on a random basis. An alternative approach is to use the [k-means++ algorithm](http://en.wikipedia.org/wiki/K-means%2B%2B), in which after the first centroid is selected randomly, the subsequent centroids are selected with probability proportional to the distance from the randomly selected centroid.
32 |
33 | #### Downsampling
34 |
35 | The k-means algorithm has a worst case runtime that is super-polynomial in the input size[[15](http://en.wikipedia.org/wiki/K-means%2B%2B)], so sampling large numbers of pixels is a problem. Images are automatically downsampled such that the total number of pixels is less than or equal to a specified maximum number of pixels to sample. The value I've been using is `1000`, which is a good balance between accurate results and runtime.
36 |
37 | ### Implementation
38 |
39 | Everything is implemented in Swift except for the functions that convert between color spaces, which use GLKit and thus must be written in C (since Swift doesn't support C unions at this time).
40 |
41 | ### Apps
42 |
43 | The project includes Mac and iOS apps that can be used to see the results of the algorithm and to run a simple benchmark.
44 |
45 | 
46 | 
47 |
48 | ### Contact
49 |
50 | * Indragie Karunaratne
51 | * [@indragie](http://twitter.com/indragie)
52 | * [http://indragie.com](http://indragie.com)
53 |
54 | ### License
55 |
56 | Licensed under the MIT License.
57 |
58 | ### References
59 |
60 | 1 [http://en.wikipedia.org/wiki/Lab_color_space#Advantages](http://en.wikipedia.org/wiki/Lab_color_space#Advantages)
61 | 2 [http://en.wikipedia.org/wiki/Lab_color_space#RGB_and_CMYK_conversions](http://en.wikipedia.org/wiki/Lab_color_space#RGB_and_CMYK_conversions)
62 | 3 [https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGColorSpace/index.html](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGColorSpace/index.html)
63 | 4 [http://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29](http://en.wikipedia.org/wiki/SRGB#The_forward_transformation_.28CIE_xyY_or_CIE_XYZ_to_sRGB.29)
64 | 5 [http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions](http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions)
65 | 6 [http://en.wikipedia.org/wiki/Illuminant_D65](http://en.wikipedia.org/wiki/Illuminant_D65)
66 | 7 [http://www.easyrgb.com/index.php?X=MATH&H=15#text15](http://www.easyrgb.com/index.php?X=MATH&H=15#text15)
67 | 8 [http://en.wikipedia.org/wiki/Color_difference#CIE76](http://en.wikipedia.org/wiki/Color_difference#CIE76)
68 | 9 [http://en.wikipedia.org/wiki/Color_difference#CIEDE2000](http://en.wikipedia.org/wiki/Color_difference#CIE94)
69 | 10 [http://en.wikipedia.org/wiki/Color_difference#CIEDE2000](http://en.wikipedia.org/wiki/Color_difference#CIEDE2000)
70 | 11 [http://en.wikipedia.org/wiki/K-means_clustering](http://en.wikipedia.org/wiki/K-means_clustering)
71 | 12 [http://users.eecs.northwestern.edu/~wkliao/Kmeans/](http://users.eecs.northwestern.edu/~wkliao/Kmeans/)
72 | 13 [http://cs.smu.ca/~r_zhang/code/kmeans.c](http://cs.smu.ca/~r_zhang/code/kmeans.c)
73 | 14 [http://en.wikipedia.org/wiki/Determining_the_number_of_clusters_in_a_data_set#cite_note-1](http://en.wikipedia.org/wiki/Determining_the_number_of_clusters_in_a_data_set#cite_note-1)
74 | 15 [http://en.wikipedia.org/wiki/K-means%2B%2B](http://en.wikipedia.org/wiki/K-means%2B%2B)
--------------------------------------------------------------------------------
/DominantColor/Shared/DominantColors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DominantColors.swift
3 | // DominantColor
4 | //
5 | // Created by Indragie on 12/20/14.
6 | // Copyright (c) 2014 Indragie Karunaratne. All rights reserved.
7 | //
8 |
9 | #if os(OSX)
10 | import Foundation
11 | #elseif os(iOS)
12 | import UIKit
13 | #endif
14 |
15 | // MARK: Bitmaps
16 |
17 | private struct RGBAPixel {
18 | let r: UInt8
19 | let g: UInt8
20 | let b: UInt8
21 | let a: UInt8
22 | }
23 |
24 | extension RGBAPixel: Hashable {
25 | private var hashValue: Int {
26 | return (((Int(r) << 8) | Int(g)) << 8) | Int(b)
27 | }
28 | }
29 |
30 | private func ==(lhs: RGBAPixel, rhs: RGBAPixel) -> Bool {
31 | return lhs.r == rhs.r && lhs.g == rhs.g && lhs.b == rhs.b
32 | }
33 |
34 | private func createRGBAContext(width: Int, height: Int) -> CGContext {
35 | return CGBitmapContextCreate(
36 | nil,
37 | width,
38 | height,
39 | 8, // bits per component
40 | width * 4, // bytes per row
41 | CGColorSpaceCreateDeviceRGB(),
42 | CGBitmapInfo(CGImageAlphaInfo.PremultipliedLast.rawValue)
43 | )
44 | }
45 |
46 | // Enumerates over all of the pixels in an RGBA bitmap context
47 | // in the order that they are stored in memory, for faster access.
48 | //
49 | // From: https://www.mikeash.com/pyblog/friday-qa-2012-08-31-obtaining-and-interpreting-image-data.html
50 | private func enumerateRGBAContext(context: CGContext, handler: (Int, Int, RGBAPixel) -> Void) {
51 | let (width, height) = (CGBitmapContextGetWidth(context), CGBitmapContextGetHeight(context))
52 | let data = unsafeBitCast(CGBitmapContextGetData(context), UnsafeMutablePointer.self)
53 | for y in 0.. CGColor {
63 | return CGColorCreate(CGColorSpaceCreateDeviceRGB(), [CGFloat(rgbVector.x), CGFloat(rgbVector.y), CGFloat(rgbVector.z), 1.0])
64 | }
65 |
66 | private extension RGBAPixel {
67 | func toRGBVector() -> INVector3 {
68 | return INVector3(
69 | x: Float(r) / Float(UInt8.max),
70 | y: Float(g) / Float(UInt8.max),
71 | z: Float(b) / Float(UInt8.max)
72 | )
73 | }
74 | }
75 |
76 | // MARK: Clustering
77 |
78 | extension INVector3 : ClusteredType {}
79 |
80 | // MARK: Main
81 |
82 | public enum GroupingAccuracy {
83 | case Low // CIE 76 - Euclidian distance
84 | case Medium // CIE 94 - Perceptual non-uniformity corrections
85 | case High // CIE 2000 - Additional corrections for neutral colors, lightness, chroma, and hue
86 | }
87 |
88 | struct DefaultParameterValues {
89 | static var maxSampledPixels: Int = 1000
90 | static var accuracy: GroupingAccuracy = .Medium
91 | static var seed: UInt32 = 3571
92 | static var memoizeConversions: Bool = false
93 | }
94 |
95 | /**
96 | Computes the dominant colors in an image
97 |
98 | :param: image The image
99 | :param: maxSampledPixels Maximum number of pixels to sample in the image. If
100 | the total number of pixels in the image exceeds this
101 | value, it will be downsampled to meet the constraint.
102 | :param: accuracy Level of accuracy to use when grouping similar colors.
103 | Higher accuracy will come with a performance tradeoff.
104 | :param: seed Seed to use when choosing the initial points for grouping
105 | of similar colors. The same seed is guaranteed to return
106 | the same colors every time.
107 | :param: memoizeConversions Whether to memoize conversions from RGB to the LAB color
108 | space (used for grouping similar colors). Memoization
109 | will only yield better performance for large values of
110 | `maxSampledPixels` in images that are primarily comprised
111 | of flat colors. If this information about the image is
112 | not known beforehand, it is best to not memoize.
113 |
114 | :returns: A list of dominant colors in the image sorted from most dominant to
115 | least dominant.
116 | */
117 | public func dominantColorsInImage(
118 | image: CGImage,
119 | maxSampledPixels: Int = DefaultParameterValues.maxSampledPixels,
120 | accuracy: GroupingAccuracy = DefaultParameterValues.accuracy,
121 | seed: UInt32 = DefaultParameterValues.seed,
122 | memoizeConversions: Bool = DefaultParameterValues.memoizeConversions
123 | ) -> [CGColor] {
124 |
125 | let (width, height) = (CGImageGetWidth(image), CGImageGetHeight(image))
126 | let (scaledWidth, scaledHeight) = scaledDimensionsForPixelLimit(maxSampledPixels, width, height)
127 |
128 | // Downsample the image if necessary, so that the total number of
129 | // pixels sampled does not exceed the specified maximum.
130 | let context = createRGBAContext(scaledWidth, scaledHeight)
131 | CGContextDrawImage(context, CGRect(x: 0, y: 0, width: Int(scaledWidth), height: Int(scaledHeight)), image)
132 |
133 | // Get the RGB colors from the bitmap context, ignoring any pixels
134 | // that have alpha transparency.
135 | // Also convert the colors to the LAB color space
136 | var labValues = [INVector3]()
137 | labValues.reserveCapacity(Int(scaledWidth * scaledHeight))
138 |
139 | let RGBToLAB: RGBAPixel -> INVector3 = {
140 | let f: RGBAPixel -> INVector3 = { IN_RGBToLAB($0.toRGBVector()) }
141 | return memoizeConversions ? memoize(f) : f
142 | }()
143 | enumerateRGBAContext(context) { (_, _, pixel) in
144 | if pixel.a == UInt8.max {
145 | labValues.append(RGBToLAB(pixel))
146 | }
147 | }
148 | // Cluster the colors using the k-means algorithm
149 | let k = selectKForElements(labValues)
150 | var clusters = kmeans(labValues, k, seed, distanceForAccuracy(accuracy))
151 |
152 | // Sort the clusters by size in descending order so that the
153 | // most dominant colors come first.
154 | clusters.sort { $0.size > $1.size }
155 |
156 | return clusters.map { RGBVectorToCGColor(IN_LABToRGB($0.centroid)) }
157 | }
158 |
159 | private func distanceForAccuracy(accuracy: GroupingAccuracy) -> (INVector3, INVector3) -> Float {
160 | switch accuracy {
161 | case .Low:
162 | return CIE76SquaredColorDifference
163 | case .Medium:
164 | return CIE94SquaredColorDifference()
165 | case .High:
166 | return CIE2000SquaredColorDifference()
167 | }
168 | }
169 |
170 | // Computes the proportionally scaled dimensions such that the
171 | // total number of pixels does not exceed the specified limit.
172 | private func scaledDimensionsForPixelLimit(limit: Int, width: Int, height: Int) -> (Int, Int) {
173 | if (width * height > limit) {
174 | let ratio = Float(width) / Float(height)
175 | let maxWidth = sqrtf(ratio * Float(limit))
176 | return (Int(maxWidth), Int(Float(limit) / maxWidth))
177 | }
178 | return (width, height)
179 | }
180 |
181 | private func selectKForElements(elements: [T]) -> Int {
182 | // Seems like a magic number...
183 | return 16
184 | }
185 |
--------------------------------------------------------------------------------
/DominantColor/iOS/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/DominantColor.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 2F3D09861A4C212A001ED0BF /* Memoization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F3D09851A4C212A001ED0BF /* Memoization.swift */; };
11 | 2F3D09871A4C212A001ED0BF /* Memoization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F3D09851A4C212A001ED0BF /* Memoization.swift */; };
12 | 723DE5291A4934E200C357E3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5261A4934E200C357E3 /* AppDelegate.swift */; };
13 | 723DE52A1A4934E200C357E3 /* DragAndDropImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5271A4934E200C357E3 /* DragAndDropImageView.swift */; };
14 | 723DE52B1A4934E200C357E3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 723DE5281A4934E200C357E3 /* Images.xcassets */; };
15 | 723DE52E1A4934F800C357E3 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 723DE52C1A4934F800C357E3 /* MainMenu.xib */; };
16 | 723DE54D1A49353C00C357E3 /* DominantColor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE5361A49353C00C357E3 /* DominantColor.framework */; };
17 | 723DE54E1A49353C00C357E3 /* DominantColor.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE5361A49353C00C357E3 /* DominantColor.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
18 | 723DE5591A49358F00C357E3 /* DominantColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5581A49358F00C357E3 /* DominantColor.h */; settings = {ATTRIBUTES = (Public, ); }; };
19 | 723DE55A1A49360A00C357E3 /* INVector3.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5181A49348D00C357E3 /* INVector3.h */; settings = {ATTRIBUTES = (Public, ); }; };
20 | 723DE55B1A49360C00C357E3 /* ColorSpaceConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5151A49348D00C357E3 /* ColorSpaceConversion.h */; settings = {ATTRIBUTES = (Public, ); }; };
21 | 723DE55C1A49360F00C357E3 /* ColorDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5141A49348D00C357E3 /* ColorDifference.swift */; };
22 | 723DE55D1A49361100C357E3 /* DominantColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5171A49348D00C357E3 /* DominantColors.swift */; };
23 | 723DE55E1A49361200C357E3 /* KMeans.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE51A1A49348D00C357E3 /* KMeans.swift */; };
24 | 723DE55F1A49361700C357E3 /* INVector3.m in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5191A49348D00C357E3 /* INVector3.m */; };
25 | 723DE5601A49361900C357E3 /* ColorSpaceConversion.m in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5161A49348D00C357E3 /* ColorSpaceConversion.m */; };
26 | 723DE5611A49372D00C357E3 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D797DE1A43F89000D32E7C /* GLKit.framework */; };
27 | 723DE5691A49385C00C357E3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5661A49385C00C357E3 /* AppDelegate.swift */; };
28 | 723DE56A1A49385C00C357E3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 723DE5671A49385C00C357E3 /* Images.xcassets */; };
29 | 723DE56B1A49385C00C357E3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5681A49385C00C357E3 /* ViewController.swift */; };
30 | 723DE5701A49386B00C357E3 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 723DE56C1A49386B00C357E3 /* LaunchScreen.xib */; };
31 | 723DE5711A49386B00C357E3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 723DE56E1A49386B00C357E3 /* Main.storyboard */; };
32 | 723DE5B61A4938DD00C357E3 /* DominantColor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE59F1A4938DD00C357E3 /* DominantColor.framework */; };
33 | 723DE5B71A4938DD00C357E3 /* DominantColor.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE59F1A4938DD00C357E3 /* DominantColor.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
34 | 723DE5BF1A49394D00C357E3 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 723DE5BE1A49394D00C357E3 /* GLKit.framework */; };
35 | 723DE5C01A49395A00C357E3 /* DominantColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5581A49358F00C357E3 /* DominantColor.h */; settings = {ATTRIBUTES = (Public, ); }; };
36 | 723DE5C11A49395C00C357E3 /* ColorSpaceConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5151A49348D00C357E3 /* ColorSpaceConversion.h */; settings = {ATTRIBUTES = (Public, ); }; };
37 | 723DE5C21A49395D00C357E3 /* INVector3.h in Headers */ = {isa = PBXBuildFile; fileRef = 723DE5181A49348D00C357E3 /* INVector3.h */; settings = {ATTRIBUTES = (Public, ); }; };
38 | 723DE5C31A49399E00C357E3 /* ColorDifference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5141A49348D00C357E3 /* ColorDifference.swift */; };
39 | 723DE5C41A4939A000C357E3 /* DominantColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5171A49348D00C357E3 /* DominantColors.swift */; };
40 | 723DE5C51A4939A200C357E3 /* KMeans.swift in Sources */ = {isa = PBXBuildFile; fileRef = 723DE51A1A49348D00C357E3 /* KMeans.swift */; };
41 | 723DE5C61A4939A300C357E3 /* ColorSpaceConversion.m in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5161A49348D00C357E3 /* ColorSpaceConversion.m */; };
42 | 723DE5C71A4939A500C357E3 /* INVector3.m in Sources */ = {isa = PBXBuildFile; fileRef = 723DE5191A49348D00C357E3 /* INVector3.m */; };
43 | 72431C0F1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */; };
44 | 72431C101A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */; };
45 | 724C2DCD1A4CD64600472402 /* PlatformExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */; };
46 | 724C2DCE1A4CD64600472402 /* PlatformExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */; };
47 | /* End PBXBuildFile section */
48 |
49 | /* Begin PBXContainerItemProxy section */
50 | 723DE54B1A49353C00C357E3 /* PBXContainerItemProxy */ = {
51 | isa = PBXContainerItemProxy;
52 | containerPortal = 72D797B01A43F44D00D32E7C /* Project object */;
53 | proxyType = 1;
54 | remoteGlobalIDString = 723DE5351A49353C00C357E3;
55 | remoteInfo = DominantColor;
56 | };
57 | 723DE5B41A4938DD00C357E3 /* PBXContainerItemProxy */ = {
58 | isa = PBXContainerItemProxy;
59 | containerPortal = 72D797B01A43F44D00D32E7C /* Project object */;
60 | proxyType = 1;
61 | remoteGlobalIDString = 723DE59E1A4938DD00C357E3;
62 | remoteInfo = DominantColor;
63 | };
64 | /* End PBXContainerItemProxy section */
65 |
66 | /* Begin PBXCopyFilesBuildPhase section */
67 | 723DE5521A49353C00C357E3 /* Embed Frameworks */ = {
68 | isa = PBXCopyFilesBuildPhase;
69 | buildActionMask = 2147483647;
70 | dstPath = "";
71 | dstSubfolderSpec = 10;
72 | files = (
73 | 723DE54E1A49353C00C357E3 /* DominantColor.framework in Embed Frameworks */,
74 | );
75 | name = "Embed Frameworks";
76 | runOnlyForDeploymentPostprocessing = 0;
77 | };
78 | 723DE5921A4938B500C357E3 /* Embed Frameworks */ = {
79 | isa = PBXCopyFilesBuildPhase;
80 | buildActionMask = 2147483647;
81 | dstPath = "";
82 | dstSubfolderSpec = 10;
83 | files = (
84 | 723DE5B71A4938DD00C357E3 /* DominantColor.framework in Embed Frameworks */,
85 | );
86 | name = "Embed Frameworks";
87 | runOnlyForDeploymentPostprocessing = 0;
88 | };
89 | /* End PBXCopyFilesBuildPhase section */
90 |
91 | /* Begin PBXFileReference section */
92 | 2F3D09851A4C212A001ED0BF /* Memoization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Memoization.swift; sourceTree = ""; };
93 | 723DE5141A49348D00C357E3 /* ColorDifference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorDifference.swift; sourceTree = ""; };
94 | 723DE5151A49348D00C357E3 /* ColorSpaceConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorSpaceConversion.h; sourceTree = ""; };
95 | 723DE5161A49348D00C357E3 /* ColorSpaceConversion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ColorSpaceConversion.m; sourceTree = ""; };
96 | 723DE5171A49348D00C357E3 /* DominantColors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DominantColors.swift; sourceTree = ""; };
97 | 723DE5181A49348D00C357E3 /* INVector3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INVector3.h; sourceTree = ""; };
98 | 723DE5191A49348D00C357E3 /* INVector3.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = INVector3.m; sourceTree = ""; };
99 | 723DE51A1A49348D00C357E3 /* KMeans.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KMeans.swift; sourceTree = ""; };
100 | 723DE5261A4934E200C357E3 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Mac/AppDelegate.swift; sourceTree = ""; };
101 | 723DE5271A4934E200C357E3 /* DragAndDropImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DragAndDropImageView.swift; path = Mac/DragAndDropImageView.swift; sourceTree = ""; };
102 | 723DE5281A4934E200C357E3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Mac/Images.xcassets; sourceTree = ""; };
103 | 723DE52D1A4934F800C357E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Mac/Base.lproj/MainMenu.xib; sourceTree = ""; };
104 | 723DE52F1A49350000C357E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Mac/Info.plist; sourceTree = ""; };
105 | 723DE5361A49353C00C357E3 /* DominantColor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DominantColor.framework; sourceTree = BUILT_PRODUCTS_DIR; };
106 | 723DE5561A49357D00C357E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Shared/Info.plist; sourceTree = ""; };
107 | 723DE5581A49358F00C357E3 /* DominantColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DominantColor.h; sourceTree = ""; };
108 | 723DE5641A4937AD00C357E3 /* ExampleMac-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ExampleMac-Bridging-Header.h"; path = "Mac/ExampleMac-Bridging-Header.h"; sourceTree = ""; };
109 | 723DE5661A49385C00C357E3 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = DominantColor/iOS/AppDelegate.swift; sourceTree = SOURCE_ROOT; };
110 | 723DE5671A49385C00C357E3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = DominantColor/iOS/Images.xcassets; sourceTree = SOURCE_ROOT; };
111 | 723DE5681A49385C00C357E3 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = DominantColor/iOS/ViewController.swift; sourceTree = SOURCE_ROOT; };
112 | 723DE56D1A49386B00C357E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = DominantColor/iOS/Base.lproj/LaunchScreen.xib; sourceTree = SOURCE_ROOT; };
113 | 723DE56F1A49386B00C357E3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = DominantColor/iOS/Base.lproj/Main.storyboard; sourceTree = SOURCE_ROOT; };
114 | 723DE5721A49387800C357E3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = DominantColor/iOS/Info.plist; sourceTree = SOURCE_ROOT; };
115 | 723DE5741A49388400C357E3 /* ExampleiOS-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ExampleiOS-Bridging-Header.h"; path = "DominantColor/iOS/ExampleiOS-Bridging-Header.h"; sourceTree = SOURCE_ROOT; };
116 | 723DE59F1A4938DD00C357E3 /* DominantColor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DominantColor.framework; sourceTree = BUILT_PRODUCTS_DIR; };
117 | 723DE5BE1A49394D00C357E3 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk/System/Library/Frameworks/GLKit.framework; sourceTree = DEVELOPER_DIR; };
118 | 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = INVector3SwiftExtensions.swift; sourceTree = ""; };
119 | 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlatformExtensions.swift; sourceTree = ""; };
120 | 72D797B81A43F44D00D32E7C /* ExampleMac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleMac.app; sourceTree = BUILT_PRODUCTS_DIR; };
121 | 72D797DE1A43F89000D32E7C /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; };
122 | 898845D21A490CE000003EF2 /* ExampleiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleiOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
123 | /* End PBXFileReference section */
124 |
125 | /* Begin PBXFrameworksBuildPhase section */
126 | 723DE5321A49353C00C357E3 /* Frameworks */ = {
127 | isa = PBXFrameworksBuildPhase;
128 | buildActionMask = 2147483647;
129 | files = (
130 | 723DE5611A49372D00C357E3 /* GLKit.framework in Frameworks */,
131 | );
132 | runOnlyForDeploymentPostprocessing = 0;
133 | };
134 | 723DE59B1A4938DD00C357E3 /* Frameworks */ = {
135 | isa = PBXFrameworksBuildPhase;
136 | buildActionMask = 2147483647;
137 | files = (
138 | 723DE5BF1A49394D00C357E3 /* GLKit.framework in Frameworks */,
139 | );
140 | runOnlyForDeploymentPostprocessing = 0;
141 | };
142 | 72D797B51A43F44D00D32E7C /* Frameworks */ = {
143 | isa = PBXFrameworksBuildPhase;
144 | buildActionMask = 2147483647;
145 | files = (
146 | 723DE54D1A49353C00C357E3 /* DominantColor.framework in Frameworks */,
147 | );
148 | runOnlyForDeploymentPostprocessing = 0;
149 | };
150 | 898845CF1A490CE000003EF2 /* Frameworks */ = {
151 | isa = PBXFrameworksBuildPhase;
152 | buildActionMask = 2147483647;
153 | files = (
154 | 723DE5B61A4938DD00C357E3 /* DominantColor.framework in Frameworks */,
155 | );
156 | runOnlyForDeploymentPostprocessing = 0;
157 | };
158 | /* End PBXFrameworksBuildPhase section */
159 |
160 | /* Begin PBXGroup section */
161 | 723DE5131A49348D00C357E3 /* Shared */ = {
162 | isa = PBXGroup;
163 | children = (
164 | 723DE5581A49358F00C357E3 /* DominantColor.h */,
165 | 723DE5141A49348D00C357E3 /* ColorDifference.swift */,
166 | 723DE5171A49348D00C357E3 /* DominantColors.swift */,
167 | 72431C0E1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift */,
168 | 723DE51A1A49348D00C357E3 /* KMeans.swift */,
169 | 2F3D09851A4C212A001ED0BF /* Memoization.swift */,
170 | 724C2DCC1A4CD64600472402 /* PlatformExtensions.swift */,
171 | 723DE5151A49348D00C357E3 /* ColorSpaceConversion.h */,
172 | 723DE5161A49348D00C357E3 /* ColorSpaceConversion.m */,
173 | 723DE5181A49348D00C357E3 /* INVector3.h */,
174 | 723DE5191A49348D00C357E3 /* INVector3.m */,
175 | 723DE5381A49353C00C357E3 /* Supporting Files */,
176 | );
177 | name = Shared;
178 | path = DominantColor/Shared;
179 | sourceTree = "";
180 | };
181 | 723DE5381A49353C00C357E3 /* Supporting Files */ = {
182 | isa = PBXGroup;
183 | children = (
184 | 723DE5561A49357D00C357E3 /* Info.plist */,
185 | );
186 | name = "Supporting Files";
187 | path = ..;
188 | sourceTree = "";
189 | };
190 | 72B92ADA1A44CCCD00A2C24C /* Frameworks */ = {
191 | isa = PBXGroup;
192 | children = (
193 | 723DE5BE1A49394D00C357E3 /* GLKit.framework */,
194 | 72D797DE1A43F89000D32E7C /* GLKit.framework */,
195 | );
196 | name = Frameworks;
197 | path = DominantColor;
198 | sourceTree = "";
199 | };
200 | 72D797AF1A43F44D00D32E7C = {
201 | isa = PBXGroup;
202 | children = (
203 | 723DE5131A49348D00C357E3 /* Shared */,
204 | 72D797BA1A43F44D00D32E7C /* Mac */,
205 | 898845D31A490CE000003EF2 /* iOS */,
206 | 72B92ADA1A44CCCD00A2C24C /* Frameworks */,
207 | 72D797B91A43F44D00D32E7C /* Products */,
208 | );
209 | sourceTree = "";
210 | };
211 | 72D797B91A43F44D00D32E7C /* Products */ = {
212 | isa = PBXGroup;
213 | children = (
214 | 72D797B81A43F44D00D32E7C /* ExampleMac.app */,
215 | 898845D21A490CE000003EF2 /* ExampleiOS.app */,
216 | 723DE5361A49353C00C357E3 /* DominantColor.framework */,
217 | 723DE59F1A4938DD00C357E3 /* DominantColor.framework */,
218 | );
219 | name = Products;
220 | sourceTree = "";
221 | };
222 | 72D797BA1A43F44D00D32E7C /* Mac */ = {
223 | isa = PBXGroup;
224 | children = (
225 | 723DE5261A4934E200C357E3 /* AppDelegate.swift */,
226 | 723DE5271A4934E200C357E3 /* DragAndDropImageView.swift */,
227 | 723DE5641A4937AD00C357E3 /* ExampleMac-Bridging-Header.h */,
228 | 723DE5281A4934E200C357E3 /* Images.xcassets */,
229 | 723DE52C1A4934F800C357E3 /* MainMenu.xib */,
230 | 72D797BB1A43F44D00D32E7C /* Supporting Files */,
231 | );
232 | name = Mac;
233 | path = DominantColor;
234 | sourceTree = "";
235 | };
236 | 72D797BB1A43F44D00D32E7C /* Supporting Files */ = {
237 | isa = PBXGroup;
238 | children = (
239 | 723DE52F1A49350000C357E3 /* Info.plist */,
240 | );
241 | name = "Supporting Files";
242 | sourceTree = "";
243 | };
244 | 898845D31A490CE000003EF2 /* iOS */ = {
245 | isa = PBXGroup;
246 | children = (
247 | 723DE5661A49385C00C357E3 /* AppDelegate.swift */,
248 | 723DE5681A49385C00C357E3 /* ViewController.swift */,
249 | 723DE5741A49388400C357E3 /* ExampleiOS-Bridging-Header.h */,
250 | 723DE5671A49385C00C357E3 /* Images.xcassets */,
251 | 723DE56C1A49386B00C357E3 /* LaunchScreen.xib */,
252 | 723DE56E1A49386B00C357E3 /* Main.storyboard */,
253 | 898845D41A490CE000003EF2 /* Supporting Files */,
254 | );
255 | name = iOS;
256 | path = "DominantColor-iOS";
257 | sourceTree = "";
258 | };
259 | 898845D41A490CE000003EF2 /* Supporting Files */ = {
260 | isa = PBXGroup;
261 | children = (
262 | 723DE5721A49387800C357E3 /* Info.plist */,
263 | );
264 | name = "Supporting Files";
265 | sourceTree = "";
266 | };
267 | /* End PBXGroup section */
268 |
269 | /* Begin PBXHeadersBuildPhase section */
270 | 723DE5331A49353C00C357E3 /* Headers */ = {
271 | isa = PBXHeadersBuildPhase;
272 | buildActionMask = 2147483647;
273 | files = (
274 | 723DE5591A49358F00C357E3 /* DominantColor.h in Headers */,
275 | 723DE55B1A49360C00C357E3 /* ColorSpaceConversion.h in Headers */,
276 | 723DE55A1A49360A00C357E3 /* INVector3.h in Headers */,
277 | );
278 | runOnlyForDeploymentPostprocessing = 0;
279 | };
280 | 723DE59C1A4938DD00C357E3 /* Headers */ = {
281 | isa = PBXHeadersBuildPhase;
282 | buildActionMask = 2147483647;
283 | files = (
284 | 723DE5C01A49395A00C357E3 /* DominantColor.h in Headers */,
285 | 723DE5C11A49395C00C357E3 /* ColorSpaceConversion.h in Headers */,
286 | 723DE5C21A49395D00C357E3 /* INVector3.h in Headers */,
287 | );
288 | runOnlyForDeploymentPostprocessing = 0;
289 | };
290 | /* End PBXHeadersBuildPhase section */
291 |
292 | /* Begin PBXNativeTarget section */
293 | 723DE5351A49353C00C357E3 /* DominantColor-Mac */ = {
294 | isa = PBXNativeTarget;
295 | buildConfigurationList = 723DE54F1A49353C00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-Mac" */;
296 | buildPhases = (
297 | 723DE5311A49353C00C357E3 /* Sources */,
298 | 723DE5321A49353C00C357E3 /* Frameworks */,
299 | 723DE5331A49353C00C357E3 /* Headers */,
300 | 723DE5341A49353C00C357E3 /* Resources */,
301 | );
302 | buildRules = (
303 | );
304 | dependencies = (
305 | );
306 | name = "DominantColor-Mac";
307 | productName = DominantColor;
308 | productReference = 723DE5361A49353C00C357E3 /* DominantColor.framework */;
309 | productType = "com.apple.product-type.framework";
310 | };
311 | 723DE59E1A4938DD00C357E3 /* DominantColor-iOS */ = {
312 | isa = PBXNativeTarget;
313 | buildConfigurationList = 723DE5B81A4938DD00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-iOS" */;
314 | buildPhases = (
315 | 723DE59A1A4938DD00C357E3 /* Sources */,
316 | 723DE59B1A4938DD00C357E3 /* Frameworks */,
317 | 723DE59C1A4938DD00C357E3 /* Headers */,
318 | 723DE59D1A4938DD00C357E3 /* Resources */,
319 | );
320 | buildRules = (
321 | );
322 | dependencies = (
323 | );
324 | name = "DominantColor-iOS";
325 | productName = DominantColor;
326 | productReference = 723DE59F1A4938DD00C357E3 /* DominantColor.framework */;
327 | productType = "com.apple.product-type.framework";
328 | };
329 | 72D797B71A43F44D00D32E7C /* ExampleMac */ = {
330 | isa = PBXNativeTarget;
331 | buildConfigurationList = 72D797D21A43F44D00D32E7C /* Build configuration list for PBXNativeTarget "ExampleMac" */;
332 | buildPhases = (
333 | 72D797B41A43F44D00D32E7C /* Sources */,
334 | 72D797B51A43F44D00D32E7C /* Frameworks */,
335 | 72D797B61A43F44D00D32E7C /* Resources */,
336 | 723DE5521A49353C00C357E3 /* Embed Frameworks */,
337 | );
338 | buildRules = (
339 | );
340 | dependencies = (
341 | 723DE54C1A49353C00C357E3 /* PBXTargetDependency */,
342 | );
343 | name = ExampleMac;
344 | productName = DominantColor;
345 | productReference = 72D797B81A43F44D00D32E7C /* ExampleMac.app */;
346 | productType = "com.apple.product-type.application";
347 | };
348 | 898845D11A490CE000003EF2 /* ExampleiOS */ = {
349 | isa = PBXNativeTarget;
350 | buildConfigurationList = 898845F21A490CE000003EF2 /* Build configuration list for PBXNativeTarget "ExampleiOS" */;
351 | buildPhases = (
352 | 898845CE1A490CE000003EF2 /* Sources */,
353 | 898845CF1A490CE000003EF2 /* Frameworks */,
354 | 898845D01A490CE000003EF2 /* Resources */,
355 | 723DE5921A4938B500C357E3 /* Embed Frameworks */,
356 | );
357 | buildRules = (
358 | );
359 | dependencies = (
360 | 723DE5B51A4938DD00C357E3 /* PBXTargetDependency */,
361 | );
362 | name = ExampleiOS;
363 | productName = "DominantColor-iOS";
364 | productReference = 898845D21A490CE000003EF2 /* ExampleiOS.app */;
365 | productType = "com.apple.product-type.application";
366 | };
367 | /* End PBXNativeTarget section */
368 |
369 | /* Begin PBXProject section */
370 | 72D797B01A43F44D00D32E7C /* Project object */ = {
371 | isa = PBXProject;
372 | attributes = {
373 | LastUpgradeCheck = 0610;
374 | ORGANIZATIONNAME = "Indragie Karunaratne";
375 | TargetAttributes = {
376 | 723DE5351A49353C00C357E3 = {
377 | CreatedOnToolsVersion = 6.1.1;
378 | };
379 | 723DE59E1A4938DD00C357E3 = {
380 | CreatedOnToolsVersion = 6.1.1;
381 | };
382 | 72D797B71A43F44D00D32E7C = {
383 | CreatedOnToolsVersion = 6.1.1;
384 | };
385 | 898845D11A490CE000003EF2 = {
386 | CreatedOnToolsVersion = 6.1;
387 | };
388 | };
389 | };
390 | buildConfigurationList = 72D797B31A43F44D00D32E7C /* Build configuration list for PBXProject "DominantColor" */;
391 | compatibilityVersion = "Xcode 3.2";
392 | developmentRegion = English;
393 | hasScannedForEncodings = 0;
394 | knownRegions = (
395 | en,
396 | Base,
397 | );
398 | mainGroup = 72D797AF1A43F44D00D32E7C;
399 | productRefGroup = 72D797B91A43F44D00D32E7C /* Products */;
400 | projectDirPath = "";
401 | projectRoot = "";
402 | targets = (
403 | 72D797B71A43F44D00D32E7C /* ExampleMac */,
404 | 898845D11A490CE000003EF2 /* ExampleiOS */,
405 | 723DE5351A49353C00C357E3 /* DominantColor-Mac */,
406 | 723DE59E1A4938DD00C357E3 /* DominantColor-iOS */,
407 | );
408 | };
409 | /* End PBXProject section */
410 |
411 | /* Begin PBXResourcesBuildPhase section */
412 | 723DE5341A49353C00C357E3 /* Resources */ = {
413 | isa = PBXResourcesBuildPhase;
414 | buildActionMask = 2147483647;
415 | files = (
416 | );
417 | runOnlyForDeploymentPostprocessing = 0;
418 | };
419 | 723DE59D1A4938DD00C357E3 /* Resources */ = {
420 | isa = PBXResourcesBuildPhase;
421 | buildActionMask = 2147483647;
422 | files = (
423 | );
424 | runOnlyForDeploymentPostprocessing = 0;
425 | };
426 | 72D797B61A43F44D00D32E7C /* Resources */ = {
427 | isa = PBXResourcesBuildPhase;
428 | buildActionMask = 2147483647;
429 | files = (
430 | 723DE52B1A4934E200C357E3 /* Images.xcassets in Resources */,
431 | 723DE52E1A4934F800C357E3 /* MainMenu.xib in Resources */,
432 | );
433 | runOnlyForDeploymentPostprocessing = 0;
434 | };
435 | 898845D01A490CE000003EF2 /* Resources */ = {
436 | isa = PBXResourcesBuildPhase;
437 | buildActionMask = 2147483647;
438 | files = (
439 | 723DE5711A49386B00C357E3 /* Main.storyboard in Resources */,
440 | 723DE5701A49386B00C357E3 /* LaunchScreen.xib in Resources */,
441 | 723DE56A1A49385C00C357E3 /* Images.xcassets in Resources */,
442 | );
443 | runOnlyForDeploymentPostprocessing = 0;
444 | };
445 | /* End PBXResourcesBuildPhase section */
446 |
447 | /* Begin PBXSourcesBuildPhase section */
448 | 723DE5311A49353C00C357E3 /* Sources */ = {
449 | isa = PBXSourcesBuildPhase;
450 | buildActionMask = 2147483647;
451 | files = (
452 | 724C2DCD1A4CD64600472402 /* PlatformExtensions.swift in Sources */,
453 | 2F3D09861A4C212A001ED0BF /* Memoization.swift in Sources */,
454 | 723DE55C1A49360F00C357E3 /* ColorDifference.swift in Sources */,
455 | 72431C0F1A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */,
456 | 723DE55D1A49361100C357E3 /* DominantColors.swift in Sources */,
457 | 723DE55E1A49361200C357E3 /* KMeans.swift in Sources */,
458 | 723DE5601A49361900C357E3 /* ColorSpaceConversion.m in Sources */,
459 | 723DE55F1A49361700C357E3 /* INVector3.m in Sources */,
460 | );
461 | runOnlyForDeploymentPostprocessing = 0;
462 | };
463 | 723DE59A1A4938DD00C357E3 /* Sources */ = {
464 | isa = PBXSourcesBuildPhase;
465 | buildActionMask = 2147483647;
466 | files = (
467 | 724C2DCE1A4CD64600472402 /* PlatformExtensions.swift in Sources */,
468 | 2F3D09871A4C212A001ED0BF /* Memoization.swift in Sources */,
469 | 723DE5C31A49399E00C357E3 /* ColorDifference.swift in Sources */,
470 | 72431C101A4BCF3B00470BD7 /* INVector3SwiftExtensions.swift in Sources */,
471 | 723DE5C41A4939A000C357E3 /* DominantColors.swift in Sources */,
472 | 723DE5C51A4939A200C357E3 /* KMeans.swift in Sources */,
473 | 723DE5C61A4939A300C357E3 /* ColorSpaceConversion.m in Sources */,
474 | 723DE5C71A4939A500C357E3 /* INVector3.m in Sources */,
475 | );
476 | runOnlyForDeploymentPostprocessing = 0;
477 | };
478 | 72D797B41A43F44D00D32E7C /* Sources */ = {
479 | isa = PBXSourcesBuildPhase;
480 | buildActionMask = 2147483647;
481 | files = (
482 | 723DE5291A4934E200C357E3 /* AppDelegate.swift in Sources */,
483 | 723DE52A1A4934E200C357E3 /* DragAndDropImageView.swift in Sources */,
484 | );
485 | runOnlyForDeploymentPostprocessing = 0;
486 | };
487 | 898845CE1A490CE000003EF2 /* Sources */ = {
488 | isa = PBXSourcesBuildPhase;
489 | buildActionMask = 2147483647;
490 | files = (
491 | 723DE56B1A49385C00C357E3 /* ViewController.swift in Sources */,
492 | 723DE5691A49385C00C357E3 /* AppDelegate.swift in Sources */,
493 | );
494 | runOnlyForDeploymentPostprocessing = 0;
495 | };
496 | /* End PBXSourcesBuildPhase section */
497 |
498 | /* Begin PBXTargetDependency section */
499 | 723DE54C1A49353C00C357E3 /* PBXTargetDependency */ = {
500 | isa = PBXTargetDependency;
501 | target = 723DE5351A49353C00C357E3 /* DominantColor-Mac */;
502 | targetProxy = 723DE54B1A49353C00C357E3 /* PBXContainerItemProxy */;
503 | };
504 | 723DE5B51A4938DD00C357E3 /* PBXTargetDependency */ = {
505 | isa = PBXTargetDependency;
506 | target = 723DE59E1A4938DD00C357E3 /* DominantColor-iOS */;
507 | targetProxy = 723DE5B41A4938DD00C357E3 /* PBXContainerItemProxy */;
508 | };
509 | /* End PBXTargetDependency section */
510 |
511 | /* Begin PBXVariantGroup section */
512 | 723DE52C1A4934F800C357E3 /* MainMenu.xib */ = {
513 | isa = PBXVariantGroup;
514 | children = (
515 | 723DE52D1A4934F800C357E3 /* Base */,
516 | );
517 | name = MainMenu.xib;
518 | sourceTree = "";
519 | };
520 | 723DE56C1A49386B00C357E3 /* LaunchScreen.xib */ = {
521 | isa = PBXVariantGroup;
522 | children = (
523 | 723DE56D1A49386B00C357E3 /* Base */,
524 | );
525 | name = LaunchScreen.xib;
526 | sourceTree = "";
527 | };
528 | 723DE56E1A49386B00C357E3 /* Main.storyboard */ = {
529 | isa = PBXVariantGroup;
530 | children = (
531 | 723DE56F1A49386B00C357E3 /* Base */,
532 | );
533 | name = Main.storyboard;
534 | sourceTree = "";
535 | };
536 | /* End PBXVariantGroup section */
537 |
538 | /* Begin XCBuildConfiguration section */
539 | 723DE5501A49353C00C357E3 /* Debug */ = {
540 | isa = XCBuildConfiguration;
541 | buildSettings = {
542 | COMBINE_HIDPI_IMAGES = YES;
543 | CURRENT_PROJECT_VERSION = 1;
544 | DEFINES_MODULE = YES;
545 | DYLIB_COMPATIBILITY_VERSION = 1;
546 | DYLIB_CURRENT_VERSION = 1;
547 | DYLIB_INSTALL_NAME_BASE = "@rpath";
548 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
549 | FRAMEWORK_VERSION = A;
550 | GCC_PREPROCESSOR_DEFINITIONS = (
551 | "DEBUG=1",
552 | "$(inherited)",
553 | );
554 | INFOPLIST_FILE = DominantColor/Shared/Info.plist;
555 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
556 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
557 | PRODUCT_NAME = DominantColor;
558 | SKIP_INSTALL = YES;
559 | SWIFT_OBJC_BRIDGING_HEADER = "";
560 | VERSIONING_SYSTEM = "apple-generic";
561 | VERSION_INFO_PREFIX = "";
562 | };
563 | name = Debug;
564 | };
565 | 723DE5511A49353C00C357E3 /* Release */ = {
566 | isa = XCBuildConfiguration;
567 | buildSettings = {
568 | COMBINE_HIDPI_IMAGES = YES;
569 | CURRENT_PROJECT_VERSION = 1;
570 | DEFINES_MODULE = YES;
571 | DYLIB_COMPATIBILITY_VERSION = 1;
572 | DYLIB_CURRENT_VERSION = 1;
573 | DYLIB_INSTALL_NAME_BASE = "@rpath";
574 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
575 | FRAMEWORK_VERSION = A;
576 | INFOPLIST_FILE = DominantColor/Shared/Info.plist;
577 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
578 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
579 | PRODUCT_NAME = DominantColor;
580 | SKIP_INSTALL = YES;
581 | SWIFT_OBJC_BRIDGING_HEADER = "";
582 | VERSIONING_SYSTEM = "apple-generic";
583 | VERSION_INFO_PREFIX = "";
584 | };
585 | name = Release;
586 | };
587 | 723DE5B91A4938DD00C357E3 /* Debug */ = {
588 | isa = XCBuildConfiguration;
589 | buildSettings = {
590 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
591 | CURRENT_PROJECT_VERSION = 1;
592 | DEFINES_MODULE = YES;
593 | DYLIB_COMPATIBILITY_VERSION = 1;
594 | DYLIB_CURRENT_VERSION = 1;
595 | DYLIB_INSTALL_NAME_BASE = "@rpath";
596 | GCC_PREPROCESSOR_DEFINITIONS = (
597 | "DEBUG=1",
598 | "$(inherited)",
599 | );
600 | INFOPLIST_FILE = DominantColor/iOS/Info.plist;
601 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
602 | IPHONEOS_DEPLOYMENT_TARGET = 8.1;
603 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
604 | PRODUCT_NAME = DominantColor;
605 | SDKROOT = iphoneos;
606 | SKIP_INSTALL = YES;
607 | TARGETED_DEVICE_FAMILY = "1,2";
608 | VERSIONING_SYSTEM = "apple-generic";
609 | VERSION_INFO_PREFIX = "";
610 | };
611 | name = Debug;
612 | };
613 | 723DE5BA1A4938DD00C357E3 /* Release */ = {
614 | isa = XCBuildConfiguration;
615 | buildSettings = {
616 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
617 | CURRENT_PROJECT_VERSION = 1;
618 | DEFINES_MODULE = YES;
619 | DYLIB_COMPATIBILITY_VERSION = 1;
620 | DYLIB_CURRENT_VERSION = 1;
621 | DYLIB_INSTALL_NAME_BASE = "@rpath";
622 | INFOPLIST_FILE = DominantColor/iOS/Info.plist;
623 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
624 | IPHONEOS_DEPLOYMENT_TARGET = 8.1;
625 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
626 | PRODUCT_NAME = DominantColor;
627 | SDKROOT = iphoneos;
628 | SKIP_INSTALL = YES;
629 | TARGETED_DEVICE_FAMILY = "1,2";
630 | VALIDATE_PRODUCT = YES;
631 | VERSIONING_SYSTEM = "apple-generic";
632 | VERSION_INFO_PREFIX = "";
633 | };
634 | name = Release;
635 | };
636 | 72D797D01A43F44D00D32E7C /* Debug */ = {
637 | isa = XCBuildConfiguration;
638 | buildSettings = {
639 | ALWAYS_SEARCH_USER_PATHS = NO;
640 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
641 | CLANG_CXX_LIBRARY = "libc++";
642 | CLANG_ENABLE_MODULES = YES;
643 | CLANG_ENABLE_OBJC_ARC = YES;
644 | CLANG_WARN_BOOL_CONVERSION = YES;
645 | CLANG_WARN_CONSTANT_CONVERSION = YES;
646 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
647 | CLANG_WARN_EMPTY_BODY = YES;
648 | CLANG_WARN_ENUM_CONVERSION = YES;
649 | CLANG_WARN_INT_CONVERSION = YES;
650 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
651 | CLANG_WARN_UNREACHABLE_CODE = YES;
652 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
653 | CODE_SIGN_IDENTITY = "-";
654 | COPY_PHASE_STRIP = NO;
655 | ENABLE_STRICT_OBJC_MSGSEND = YES;
656 | GCC_C_LANGUAGE_STANDARD = gnu99;
657 | GCC_DYNAMIC_NO_PIC = NO;
658 | GCC_OPTIMIZATION_LEVEL = 0;
659 | GCC_PREPROCESSOR_DEFINITIONS = (
660 | "DEBUG=1",
661 | "$(inherited)",
662 | );
663 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
664 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
665 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
666 | GCC_WARN_UNDECLARED_SELECTOR = YES;
667 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
668 | GCC_WARN_UNUSED_FUNCTION = YES;
669 | GCC_WARN_UNUSED_VARIABLE = YES;
670 | MACOSX_DEPLOYMENT_TARGET = 10.10;
671 | MTL_ENABLE_DEBUG_INFO = YES;
672 | ONLY_ACTIVE_ARCH = YES;
673 | SDKROOT = macosx;
674 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
675 | };
676 | name = Debug;
677 | };
678 | 72D797D11A43F44D00D32E7C /* Release */ = {
679 | isa = XCBuildConfiguration;
680 | buildSettings = {
681 | ALWAYS_SEARCH_USER_PATHS = NO;
682 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
683 | CLANG_CXX_LIBRARY = "libc++";
684 | CLANG_ENABLE_MODULES = YES;
685 | CLANG_ENABLE_OBJC_ARC = YES;
686 | CLANG_WARN_BOOL_CONVERSION = YES;
687 | CLANG_WARN_CONSTANT_CONVERSION = YES;
688 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
689 | CLANG_WARN_EMPTY_BODY = YES;
690 | CLANG_WARN_ENUM_CONVERSION = YES;
691 | CLANG_WARN_INT_CONVERSION = YES;
692 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
693 | CLANG_WARN_UNREACHABLE_CODE = YES;
694 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
695 | CODE_SIGN_IDENTITY = "-";
696 | COPY_PHASE_STRIP = YES;
697 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
698 | ENABLE_NS_ASSERTIONS = NO;
699 | ENABLE_STRICT_OBJC_MSGSEND = YES;
700 | GCC_C_LANGUAGE_STANDARD = gnu99;
701 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
702 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
703 | GCC_WARN_UNDECLARED_SELECTOR = YES;
704 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
705 | GCC_WARN_UNUSED_FUNCTION = YES;
706 | GCC_WARN_UNUSED_VARIABLE = YES;
707 | MACOSX_DEPLOYMENT_TARGET = 10.10;
708 | MTL_ENABLE_DEBUG_INFO = NO;
709 | SDKROOT = macosx;
710 | };
711 | name = Release;
712 | };
713 | 72D797D31A43F44D00D32E7C /* Debug */ = {
714 | isa = XCBuildConfiguration;
715 | buildSettings = {
716 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
717 | CLANG_ENABLE_MODULES = YES;
718 | COMBINE_HIDPI_IMAGES = YES;
719 | HEADER_SEARCH_PATHS = (
720 | "$(inherited)",
721 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
722 | );
723 | INFOPLIST_FILE = DominantColor/Mac/Info.plist;
724 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
725 | PRODUCT_NAME = "$(TARGET_NAME)";
726 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/Mac/ExampleMac-Bridging-Header.h";
727 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
728 | };
729 | name = Debug;
730 | };
731 | 72D797D41A43F44D00D32E7C /* Release */ = {
732 | isa = XCBuildConfiguration;
733 | buildSettings = {
734 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
735 | CLANG_ENABLE_MODULES = YES;
736 | COMBINE_HIDPI_IMAGES = YES;
737 | HEADER_SEARCH_PATHS = (
738 | "$(inherited)",
739 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
740 | );
741 | INFOPLIST_FILE = DominantColor/Mac/Info.plist;
742 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
743 | PRODUCT_NAME = "$(TARGET_NAME)";
744 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/Mac/ExampleMac-Bridging-Header.h";
745 | };
746 | name = Release;
747 | };
748 | 898845EE1A490CE000003EF2 /* Debug */ = {
749 | isa = XCBuildConfiguration;
750 | buildSettings = {
751 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
752 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
753 | GCC_PREPROCESSOR_DEFINITIONS = (
754 | "DEBUG=1",
755 | "$(inherited)",
756 | );
757 | INFOPLIST_FILE = DominantColor/iOS/Info.plist;
758 | IPHONEOS_DEPLOYMENT_TARGET = 8.1;
759 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
760 | PRODUCT_NAME = "$(TARGET_NAME)";
761 | SDKROOT = iphoneos;
762 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/iOS/ExampleiOS-Bridging-Header.h";
763 | };
764 | name = Debug;
765 | };
766 | 898845EF1A490CE000003EF2 /* Release */ = {
767 | isa = XCBuildConfiguration;
768 | buildSettings = {
769 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
770 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
771 | INFOPLIST_FILE = DominantColor/iOS/Info.plist;
772 | IPHONEOS_DEPLOYMENT_TARGET = 8.1;
773 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
774 | PRODUCT_NAME = "$(TARGET_NAME)";
775 | SDKROOT = iphoneos;
776 | SWIFT_OBJC_BRIDGING_HEADER = "DominantColor/iOS/ExampleiOS-Bridging-Header.h";
777 | VALIDATE_PRODUCT = YES;
778 | };
779 | name = Release;
780 | };
781 | /* End XCBuildConfiguration section */
782 |
783 | /* Begin XCConfigurationList section */
784 | 723DE54F1A49353C00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-Mac" */ = {
785 | isa = XCConfigurationList;
786 | buildConfigurations = (
787 | 723DE5501A49353C00C357E3 /* Debug */,
788 | 723DE5511A49353C00C357E3 /* Release */,
789 | );
790 | defaultConfigurationIsVisible = 0;
791 | defaultConfigurationName = Release;
792 | };
793 | 723DE5B81A4938DD00C357E3 /* Build configuration list for PBXNativeTarget "DominantColor-iOS" */ = {
794 | isa = XCConfigurationList;
795 | buildConfigurations = (
796 | 723DE5B91A4938DD00C357E3 /* Debug */,
797 | 723DE5BA1A4938DD00C357E3 /* Release */,
798 | );
799 | defaultConfigurationIsVisible = 0;
800 | defaultConfigurationName = Release;
801 | };
802 | 72D797B31A43F44D00D32E7C /* Build configuration list for PBXProject "DominantColor" */ = {
803 | isa = XCConfigurationList;
804 | buildConfigurations = (
805 | 72D797D01A43F44D00D32E7C /* Debug */,
806 | 72D797D11A43F44D00D32E7C /* Release */,
807 | );
808 | defaultConfigurationIsVisible = 0;
809 | defaultConfigurationName = Release;
810 | };
811 | 72D797D21A43F44D00D32E7C /* Build configuration list for PBXNativeTarget "ExampleMac" */ = {
812 | isa = XCConfigurationList;
813 | buildConfigurations = (
814 | 72D797D31A43F44D00D32E7C /* Debug */,
815 | 72D797D41A43F44D00D32E7C /* Release */,
816 | );
817 | defaultConfigurationIsVisible = 0;
818 | defaultConfigurationName = Release;
819 | };
820 | 898845F21A490CE000003EF2 /* Build configuration list for PBXNativeTarget "ExampleiOS" */ = {
821 | isa = XCConfigurationList;
822 | buildConfigurations = (
823 | 898845EE1A490CE000003EF2 /* Debug */,
824 | 898845EF1A490CE000003EF2 /* Release */,
825 | );
826 | defaultConfigurationIsVisible = 0;
827 | defaultConfigurationName = Release;
828 | };
829 | /* End XCConfigurationList section */
830 | };
831 | rootObject = 72D797B01A43F44D00D32E7C /* Project object */;
832 | }
833 |
--------------------------------------------------------------------------------
/DominantColor/Mac/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
--------------------------------------------------------------------------------