From 7d8c7fabab993062068b3cbc36c3de953b8954fa Mon Sep 17 00:00:00 2001 From: ktiays Date: Tue, 11 Oct 2022 23:42:18 +0800 Subject: [PATCH 1/3] add getting dominant colors function --- Package.swift | 2 ++ Sources/CyanImage/DominantColors.swift | 45 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 Sources/CyanImage/DominantColors.swift diff --git a/Package.swift b/Package.swift index e463de1..c4f6735 100644 --- a/Package.swift +++ b/Package.swift @@ -18,6 +18,7 @@ let package = Package( .target(name: "CyanConcurrency"), .target(name: "CyanSwiftUI", dependencies: ["CyanExtensions"]), .target(name: "CyanUI", dependencies: ["CyanSwiftUI"]), + .target(name: "CyanImage", dependencies: ["CyanExtensions"]), .target( name: "CyanKit", dependencies: [ @@ -27,6 +28,7 @@ let package = Package( "CyanConcurrency", "CyanSwiftUI", "CyanUI", + "CyanImage", ]), .testTarget( name: "CyanConcurrencyTests", diff --git a/Sources/CyanImage/DominantColors.swift b/Sources/CyanImage/DominantColors.swift new file mode 100644 index 0000000..f10c5be --- /dev/null +++ b/Sources/CyanImage/DominantColors.swift @@ -0,0 +1,45 @@ +// +// Created by ktiays on 2022/10/11. +// Copyright (c) 2022 ktiays. All rights reserved. +// + +import CoreImage +import CyanExtensions + +func dominantColor(for image: CIImage) -> [PlatformColor] { + guard let kMeansFilter = CIFilter(name: "CIKMeans") else { + return [] + } + let clusterCount = 3 + + kMeansFilter.setValue(image, forKey: kCIInputImageKey) + kMeansFilter.setValue(CIVector(cgRect: image.extent), forKey: kCIInputExtentKey) + kMeansFilter.setValue(clusterCount, forKey: "inputCount") + kMeansFilter.setValue(NSNumber(value: true), forKey: "inputPerceptual") + + guard var outputImage = kMeansFilter.outputImage else { + return [] + } + + outputImage = outputImage.settingAlphaOne(in: outputImage.extent) + + let context = CIContext() + var bitmap = [UInt8](repeating: 0, count: 4 * clusterCount) + context.render(outputImage, + toBitmap: &bitmap, + rowBytes: 4 * clusterCount, + bounds: outputImage.extent, + format: CIFormat.RGBA8, + colorSpace: image.colorSpace!) + print(bitmap) + + var dominantColors = [PlatformColor]() + + for i in 0.. Date: Fri, 14 Oct 2022 11:04:45 +0800 Subject: [PATCH 2/3] add `NSImage` extension & remove `inputPerceptual` property in dominant colors --- Sources/CyanImage/DominantColors.swift | 83 +++++++++++++++++-------- Sources/CyanImage/NSImage+CIImage.swift | 32 ++++++++++ 2 files changed, 88 insertions(+), 27 deletions(-) create mode 100644 Sources/CyanImage/NSImage+CIImage.swift diff --git a/Sources/CyanImage/DominantColors.swift b/Sources/CyanImage/DominantColors.swift index f10c5be..19db05b 100644 --- a/Sources/CyanImage/DominantColors.swift +++ b/Sources/CyanImage/DominantColors.swift @@ -6,40 +6,69 @@ import CoreImage import CyanExtensions -func dominantColor(for image: CIImage) -> [PlatformColor] { - guard let kMeansFilter = CIFilter(name: "CIKMeans") else { - return [] - } - let clusterCount = 3 - - kMeansFilter.setValue(image, forKey: kCIInputImageKey) - kMeansFilter.setValue(CIVector(cgRect: image.extent), forKey: kCIInputExtentKey) - kMeansFilter.setValue(clusterCount, forKey: "inputCount") - kMeansFilter.setValue(NSNumber(value: true), forKey: "inputPerceptual") +extension CIImage { - guard var outputImage = kMeansFilter.outputImage else { - return [] + public func dominantColor(clusterCount: Int = 5) -> [PlatformColor] { + guard let kMeansFilter = CIFilter(name: "CIKMeans") else { + return [] + } + + kMeansFilter.setValue(self, forKey: kCIInputImageKey) + kMeansFilter.setValue(CIVector(cgRect: self.extent), forKey: kCIInputExtentKey) + kMeansFilter.setValue(clusterCount, forKey: "inputCount") + kMeansFilter.setValue(20, forKey: "inputPasses") +// kMeansFilter.setValue(NSNumber(value: true), forKey: "inputPerceptual") + + guard var outputImage = kMeansFilter.outputImage else { + return [] + } + + outputImage = outputImage.settingAlphaOne(in: outputImage.extent) + + let context = CIContext() + var bitmap = [UInt8](repeating: 0, count: 4 * clusterCount) + context.render(outputImage, + toBitmap: &bitmap, + rowBytes: 4 * clusterCount, + bounds: outputImage.extent, + format: CIFormat.RGBA8, + colorSpace: self.colorSpace!) + + var dominantColors = [PlatformColor]() + + for i in 0.. [UIColor] { + CIImage(image: self)?.dominantColor(clusterCount: clusterCount) ?? [] + } - var dominantColors = [PlatformColor]() +} + +#elseif canImport(AppKit) + +import AppKit + +extension NSImage { - for i in 0.. [NSColor] { + ciImage?.dominantColor(clusterCount: clusterCount) ?? [] } - return dominantColors } +#endif diff --git a/Sources/CyanImage/NSImage+CIImage.swift b/Sources/CyanImage/NSImage+CIImage.swift new file mode 100644 index 0000000..bddeb8a --- /dev/null +++ b/Sources/CyanImage/NSImage+CIImage.swift @@ -0,0 +1,32 @@ +// +// Created by ktiays on 2022/10/14. +// Copyright (c) 2022 ktiays. All rights reserved. +// + +#if canImport(AppKit) + +import AppKit + +extension NSImage { + + public var ciImage: CIImage? { + guard let data = self.tiffRepresentation, + let bitmap = NSBitmapImageRep(data: data) else { + return nil + } + return CIImage(bitmapImageRep: bitmap) + } + +} + +extension NSImage { + + public convenience init(ciImage: CIImage) { + let imageRep = NSCIImageRep(ciImage: ciImage) + self.init(size: imageRep.size) + self.addRepresentation(imageRep) + } + +} + +#endif From f23aa84cc2270983cd0f404a1a11b2d4a1a1f461 Mon Sep 17 00:00:00 2001 From: ktiays Date: Mon, 17 Oct 2022 20:18:25 +0800 Subject: [PATCH 3/3] add constructor for platform image that using colors to initialize --- Sources/CyanImage/Color.swift | 41 ++++++++++++++++++++++++++ Sources/CyanImage/DominantColors.swift | 1 - 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 Sources/CyanImage/Color.swift diff --git a/Sources/CyanImage/Color.swift b/Sources/CyanImage/Color.swift new file mode 100644 index 0000000..ae84e94 --- /dev/null +++ b/Sources/CyanImage/Color.swift @@ -0,0 +1,41 @@ +// +// Created by ktiays on 2022/10/17. +// Copyright (c) 2022 ktiays. All rights reserved. +// + +#if canImport(UIKit) + +import UIKit + +extension UIImage { + + public convenience init?(with color: UIColor, size: CGSize = .init(width: 1, height: 1)) { + UIGraphicsBeginImageContextWithOptions(size, false, 0) + color.setFill() + UIRectFill(CGRect(origin: CGPoint.zero, size: size)) + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + guard let cgImage = image?.cgImage else { + return nil + } + self.init(cgImage: cgImage) + } + +} + +#elseif canImport(AppKit) + +import AppKit + +extension NSImage { + + public convenience init(color: NSColor, size: NSSize = .init(width: 1, height: 1)) { + self.init(size: size) + lockFocus() + color.drawSwatch(in: NSRect(origin: .zero, size: size)) + unlockFocus() + } + +} + +#endif diff --git a/Sources/CyanImage/DominantColors.swift b/Sources/CyanImage/DominantColors.swift index 19db05b..57c19c0 100644 --- a/Sources/CyanImage/DominantColors.swift +++ b/Sources/CyanImage/DominantColors.swift @@ -17,7 +17,6 @@ extension CIImage { kMeansFilter.setValue(CIVector(cgRect: self.extent), forKey: kCIInputExtentKey) kMeansFilter.setValue(clusterCount, forKey: "inputCount") kMeansFilter.setValue(20, forKey: "inputPasses") -// kMeansFilter.setValue(NSNumber(value: true), forKey: "inputPerceptual") guard var outputImage = kMeansFilter.outputImage else { return []