forked from indragiek/DominantColor
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Swift Concurrency, Unit Tests, Update Demo
- Loading branch information
1 parent
fd5e726
commit 0a6aeaf
Showing
33 changed files
with
1,106 additions
and
702 deletions.
There are no files selected for viewing
22 changes: 22 additions & 0 deletions
22
...data/xcbaselines/DominantColorTests.xcbaseline/B8FEC642-12BE-4F3F-8764-91F1D5AB1965.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>classNames</key> | ||
<dict> | ||
<key>DominantColorTests</key> | ||
<dict> | ||
<key>testPerformanceExample()</key> | ||
<dict> | ||
<key>com.apple.XCTPerformanceMetric_WallClockTime</key> | ||
<dict> | ||
<key>baselineAverage</key> | ||
<real>0.033600</real> | ||
<key>baselineIntegrationDisplayName</key> | ||
<string>Local Baseline</string> | ||
</dict> | ||
</dict> | ||
</dict> | ||
</dict> | ||
</dict> | ||
</plist> |
40 changes: 40 additions & 0 deletions
40
.swiftpm/xcode/xcshareddata/xcbaselines/DominantColorTests.xcbaseline/Info.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>runDestinationsByUUID</key> | ||
<dict> | ||
<key>B8FEC642-12BE-4F3F-8764-91F1D5AB1965</key> | ||
<dict> | ||
<key>localComputer</key> | ||
<dict> | ||
<key>busSpeedInMHz</key> | ||
<integer>400</integer> | ||
<key>cpuCount</key> | ||
<integer>1</integer> | ||
<key>cpuKind</key> | ||
<string>6-Core Intel Core i9</string> | ||
<key>cpuSpeedInMHz</key> | ||
<integer>2900</integer> | ||
<key>logicalCPUCoresPerPackage</key> | ||
<integer>12</integer> | ||
<key>modelCode</key> | ||
<string>MacBookPro15,3</string> | ||
<key>physicalCPUCoresPerPackage</key> | ||
<integer>6</integer> | ||
<key>platformIdentifier</key> | ||
<string>com.apple.platform.macosx</string> | ||
</dict> | ||
<key>targetArchitecture</key> | ||
<string>x86_64</string> | ||
<key>targetDevice</key> | ||
<dict> | ||
<key>modelCode</key> | ||
<string>iPhone15,2</string> | ||
<key>platformIdentifier</key> | ||
<string>com.apple.platform.iphonesimulator</string> | ||
</dict> | ||
</dict> | ||
</dict> | ||
</dict> | ||
</plist> |
This file was deleted.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
2 changes: 1 addition & 1 deletion
2
DominantColor.xcodeproj/xcshareddata/xcschemes/DominantColor-Mac.xcscheme
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
DominantColor.xcodeproj/xcshareddata/xcschemes/DominantColor-iOS.xcscheme
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
DominantColor.xcodeproj/xcshareddata/xcschemes/ExampleMac.xcscheme
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
DominantColor.xcodeproj/xcshareddata/xcschemes/ExampleiOS.xcscheme
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// | ||
// CGImage+DominantColors.swift | ||
// DominantColor | ||
// | ||
// Created by Sam Spencer on 11/9/22. | ||
// Copyright © 2022 Indragie Karunaratne. All rights reserved. | ||
// | ||
|
||
import CoreGraphics | ||
import Foundation | ||
import simd | ||
|
||
internal extension CGImage { | ||
|
||
/// Computes the dominant colors in an image | ||
/// | ||
/// - parameter maxSampledPixels: Maximum number of pixels to sample in the image. | ||
/// If the total number of pixels in the image exceeds this value, it will be | ||
/// downsampled to meet the constraint. | ||
/// - parameter accuracy: Level of accuracy to use when grouping similar colors. | ||
/// Higher accuracy will come with a performance tradeoff. | ||
/// - parameter seed: Seed to use when choosing the initial points for grouping of | ||
/// similar colors. The same seed is guaranteed to return the same colors every | ||
/// time. | ||
/// - parameter memoizeConversions: Whether to memoize conversions from RGB to the | ||
/// LAB color space (used for grouping similar colors). Memoization will only yield | ||
/// better performance for large values of `maxSampledPixels` in images that are | ||
/// primarily comprised of flat colors. If this information about the image is not | ||
/// known beforehand, it is best to not memoize. | ||
/// | ||
/// - returns: A list of dominant colors in the image sorted from most dominant to | ||
/// least dominant. | ||
/// | ||
func dominantColorsInImage( | ||
maxSampledPixels: Int, | ||
accuracy: GroupingAccuracy, | ||
seed: UInt64, | ||
memoizeConversions: Bool | ||
) async -> [CGColor] { | ||
let calculator = DominantColorCalculator() | ||
let rgbaContext = RGBAContext() | ||
let colorSpaceConverter = ColorSpaceConverter() | ||
let kMeansProcessor = KMeans() | ||
|
||
let (scaledWidth, scaledHeight) = await calculator.scaledDimensionsForPixelLimit( | ||
maxSampledPixels, | ||
width: width, | ||
height: height | ||
) | ||
|
||
// Downsample the image if necessary, so that the total number of pixels sampled | ||
// does not exceed the specified maximum. | ||
// | ||
let context = await rgbaContext.createRGBAContext(scaledWidth, height: scaledHeight) | ||
context.draw(self, in: CGRect(x: 0, y: 0, width: Int(scaledWidth), height: Int(scaledHeight))) | ||
|
||
// Get the RGB colors from the bitmap context, ignoring any pixels that have alpha | ||
// transparency. Also convert the colors to the LAB color space. | ||
// | ||
var labValues = [simd_float3]() | ||
labValues.reserveCapacity(Int(scaledWidth * scaledHeight)) | ||
|
||
let RGBToLAB: (RGBAPixel) async -> simd_float3 = await { | ||
let f: (RGBAPixel) async -> simd_float3 = { | ||
await colorSpaceConverter.IN_RGBToLAB($0.toRGBVector()) | ||
} | ||
if memoizeConversions == true { | ||
return await memoize(f) | ||
} else { | ||
return f | ||
} | ||
}() | ||
|
||
await rgbaContext.enumerateRGBAContext(context) { _, _, pixel in | ||
if pixel.a == UInt8.max { | ||
async let rgbLab = RGBToLAB(pixel) | ||
labValues.append(await rgbLab) | ||
} | ||
} | ||
|
||
// Cluster the colors using the k-means algorithm. | ||
// | ||
let k = 16 // Magic number for now instead of: async let k = calculator.selectKForElements(labValues) | ||
async let distance = calculator.distanceForAccuracy(accuracy) | ||
var clusters = await kMeansProcessor.kmeans(labValues, k: k, seed: seed, distance: await distance) | ||
|
||
// Sort the clusters by size in descending order so that the most dominant colors | ||
// come first. | ||
// | ||
clusters.sort { $0.size > $1.size } | ||
|
||
let colorClusters = await clusters.asyncMap { cluster in | ||
async let rgbColor = colorSpaceConverter.IN_LABToRGB(cluster.centroid) | ||
return await rgbColor.fromRGBVectorToCGColor() | ||
} | ||
|
||
return colorClusters | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// | ||
// ClusteredTypeProtocol.swift | ||
// DominantColor | ||
// | ||
// Created by Sam Spencer on 11/9/22. | ||
// Copyright © 2022 Indragie Karunaratne. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
/// Represents a type that can be clustered using the k-means clustering | ||
/// algorithm. | ||
/// | ||
protocol ClusteredType { | ||
|
||
/// Used to compute average values to determine the cluster centroids. | ||
/// | ||
static func +(lhs: Self, rhs: Self) -> Self | ||
|
||
/// Used to compute average values to determine the cluster centroids. | ||
/// | ||
static func /(lhs: Self, rhs: Int) -> Self | ||
|
||
/// Identity value such that `x + identity = x`. Typically the `0` vector. | ||
/// | ||
static var identity: Self { get } | ||
|
||
} | ||
|
||
struct Cluster<T : ClusteredType> { | ||
let centroid: T | ||
let size: Int | ||
} |
Oops, something went wrong.