Skip to content

Commit

Permalink
Simplified algorithm to just detect the most used colors. Also increa…
Browse files Browse the repository at this point in the history
…sed scale size. Updated demo.
  • Loading branch information
timominous committed Dec 20, 2013
1 parent edbb01b commit 09b712f
Show file tree
Hide file tree
Showing 12 changed files with 69 additions and 87 deletions.
36 changes: 32 additions & 4 deletions Demo/TDImageColors.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@
904BA562183F0A6400F72300 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 904BA54A183F0A6400F72300 /* UIKit.framework */; };
904BA56A183F0A6400F72300 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 904BA568183F0A6400F72300 /* InfoPlist.strings */; };
904BA56C183F0A6400F72300 /* TDImageColorsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 904BA56B183F0A6400F72300 /* TDImageColorsTests.m */; };
904BA589183F1F7300F72300 /* test_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 904BA588183F1F7300F72300 /* test_image.jpg */; };
904BA58D183F1F8B00F72300 /* TDDemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 904BA58B183F1F8B00F72300 /* TDDemoViewController.m */; };
904BA58E183F1F8B00F72300 /* TDDemoViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 904BA58C183F1F8B00F72300 /* TDDemoViewController.xib */; };
9066FEF61863F034008FF19E /* test_image0.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 9066FEF01863F034008FF19E /* test_image0.jpg */; };
9066FEF71863F034008FF19E /* test_image1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 9066FEF11863F034008FF19E /* test_image1.jpg */; };
9066FEF81863F034008FF19E /* test_image2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 9066FEF21863F034008FF19E /* test_image2.jpg */; };
9066FEF91863F034008FF19E /* test_image3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 9066FEF31863F034008FF19E /* test_image3.jpg */; };
9066FEFA1863F034008FF19E /* test_image4.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 9066FEF41863F034008FF19E /* test_image4.jpg */; };
9066FEFB1863F034008FF19E /* test_image5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 9066FEF51863F034008FF19E /* test_image5.jpg */; };
90B48ABE184351A200F7C76F /* TDImageColors.m in Sources */ = {isa = PBXBuildFile; fileRef = 90B48AB9184351A200F7C76F /* TDImageColors.m */; };
90B48ABF184351A200F7C76F /* UIColor+TDAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 90B48ABB184351A200F7C76F /* UIColor+TDAdditions.m */; };
90B48AC0184351A200F7C76F /* UIImage+TDAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 90B48ABD184351A200F7C76F /* UIImage+TDAdditions.m */; };
Expand Down Expand Up @@ -54,10 +59,15 @@
904BA567183F0A6400F72300 /* TDImageColorsTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TDImageColorsTests-Info.plist"; sourceTree = "<group>"; };
904BA569183F0A6400F72300 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
904BA56B183F0A6400F72300 /* TDImageColorsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TDImageColorsTests.m; sourceTree = "<group>"; };
904BA588183F1F7300F72300 /* test_image.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_image.jpg; sourceTree = "<group>"; };
904BA58A183F1F8B00F72300 /* TDDemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TDDemoViewController.h; path = TDImageColors/TDDemoViewController.h; sourceTree = SOURCE_ROOT; };
904BA58B183F1F8B00F72300 /* TDDemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TDDemoViewController.m; path = TDImageColors/TDDemoViewController.m; sourceTree = SOURCE_ROOT; };
904BA58C183F1F8B00F72300 /* TDDemoViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = TDDemoViewController.xib; path = TDImageColors/TDDemoViewController.xib; sourceTree = SOURCE_ROOT; };
9066FEF01863F034008FF19E /* test_image0.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_image0.jpg; sourceTree = "<group>"; };
9066FEF11863F034008FF19E /* test_image1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_image1.jpg; sourceTree = "<group>"; };
9066FEF21863F034008FF19E /* test_image2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_image2.jpg; sourceTree = "<group>"; };
9066FEF31863F034008FF19E /* test_image3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_image3.jpg; sourceTree = "<group>"; };
9066FEF41863F034008FF19E /* test_image4.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_image4.jpg; sourceTree = "<group>"; };
9066FEF51863F034008FF19E /* test_image5.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_image5.jpg; sourceTree = "<group>"; };
90B48AB8184351A200F7C76F /* TDImageColors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TDImageColors.h; sourceTree = "<group>"; };
90B48AB9184351A200F7C76F /* TDImageColors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TDImageColors.m; sourceTree = "<group>"; };
90B48ABA184351A200F7C76F /* UIColor+TDAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+TDAdditions.h"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -126,10 +136,10 @@
90B48AB7184351A200F7C76F /* TDImageColors */,
904BA555183F0A6400F72300 /* TDAppDelegate.h */,
904BA556183F0A6400F72300 /* TDAppDelegate.m */,
904BA588183F1F7300F72300 /* test_image.jpg */,
904BA58A183F1F8B00F72300 /* TDDemoViewController.h */,
904BA58B183F1F8B00F72300 /* TDDemoViewController.m */,
904BA58C183F1F8B00F72300 /* TDDemoViewController.xib */,
9066FEEF1863F034008FF19E /* images */,
904BA558183F0A6400F72300 /* Images.xcassets */,
904BA54D183F0A6400F72300 /* Supporting Files */,
);
Expand Down Expand Up @@ -165,6 +175,19 @@
name = "Supporting Files";
sourceTree = "<group>";
};
9066FEEF1863F034008FF19E /* images */ = {
isa = PBXGroup;
children = (
9066FEF01863F034008FF19E /* test_image0.jpg */,
9066FEF11863F034008FF19E /* test_image1.jpg */,
9066FEF21863F034008FF19E /* test_image2.jpg */,
9066FEF31863F034008FF19E /* test_image3.jpg */,
9066FEF41863F034008FF19E /* test_image4.jpg */,
9066FEF51863F034008FF19E /* test_image5.jpg */,
);
path = images;
sourceTree = "<group>";
};
90B48AB7184351A200F7C76F /* TDImageColors */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -255,10 +278,15 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9066FEF61863F034008FF19E /* test_image0.jpg in Resources */,
9066FEF71863F034008FF19E /* test_image1.jpg in Resources */,
9066FEF81863F034008FF19E /* test_image2.jpg in Resources */,
904BA551183F0A6400F72300 /* InfoPlist.strings in Resources */,
9066FEFA1863F034008FF19E /* test_image4.jpg in Resources */,
9066FEF91863F034008FF19E /* test_image3.jpg in Resources */,
904BA58E183F1F8B00F72300 /* TDDemoViewController.xib in Resources */,
904BA559183F0A6400F72300 /* Images.xcassets in Resources */,
904BA589183F1F7300F72300 /* test_image.jpg in Resources */,
9066FEFB1863F034008FF19E /* test_image5.jpg in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
1 change: 1 addition & 0 deletions Demo/TDImageColors/TDDemoViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
@interface TDDemoViewController : UIViewController

@property (strong, nonatomic) IBOutletCollection(UIView) NSArray *colorViews;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end
6 changes: 4 additions & 2 deletions Demo/TDImageColors/TDDemoViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ - (void)viewDidLoad {
dispatch_group_t group = dispatch_group_create();

dispatch_group_enter(group);
UIImage *image = [UIImage imageNamed:@"test_image.jpg"];
NSString *imageName = [NSString stringWithFormat:@"test_image%d.jpg", arc4random_uniform(6)];
UIImage *image = [UIImage imageNamed:imageName];
self.imageView.image = image;
TDImageColors *imageColors = [[TDImageColors alloc] initWithImage:image count:5];
dispatch_group_leave(group);

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
for (UIColor *color in imageColors.colors) {
NSUInteger idx = [imageColors.colors indexOfObject:color];
Expand Down
8 changes: 3 additions & 5 deletions Demo/TDImageColors/TDDemoViewController.xib
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="4514" systemVersion="13A598" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3747"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="TDDemoViewController">
<connections>
<outlet property="imageView" destination="lbD-XP-jH3" id="LaG-Z4-8eP"/>
<outlet property="view" destination="1" id="3"/>
<outletCollection property="colorViews" destination="Fg9-it-eZE" id="DoV-FP-AKt"/>
<outletCollection property="colorViews" destination="8Tr-I5-DVt" id="T1Q-nI-JUt"/>
Expand All @@ -19,7 +20,7 @@
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="test_image.jpg" id="lbD-XP-jH3">
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="lbD-XP-jH3">
<rect key="frame" x="0.0" y="20" width="320" height="320"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
Expand Down Expand Up @@ -55,7 +56,4 @@
<simulatedScreenMetrics key="simulatedDestinationMetrics" type="retina4"/>
</view>
</objects>
<resources>
<image name="test_image.jpg" width="1600" height="1200"/>
</resources>
</document>
Binary file added Demo/TDImageColors/images/test_image0.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added Demo/TDImageColors/images/test_image2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
Binary file added Demo/TDImageColors/images/test_image5.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion TDImageColors.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'TDImageColors'
s.version = '0.1.1'
s.version = '0.2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.summary = 'iOS Library used to detect a number of most used colors in a UIImage.'
s.homepage = 'http://github.com/timominous/TDImageColors'
Expand Down
103 changes: 28 additions & 75 deletions TDImageColors/TDImageColors.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#import "TDImageColors.h"

#define TDIMAGECOLORS_SCALED_SIZE 32
#define TDIMAGECOLORS_SCALED_SIZE 100

@interface TDCountedColor : NSObject

Expand Down Expand Up @@ -42,108 +42,61 @@ - (void)detectColorsFromImage:(UIImage *)image {
if (!image)
return;
NSCountedSet *imageColors;
UIColor *backgroundColor = [self findBackroundColorOfImage:image imageColors:&imageColors];
BOOL darkBackground = [backgroundColor isDarkColor];
NSMutableArray *finalColors = [NSMutableArray arrayWithObject:backgroundColor];
[finalColors addObjectsFromArray:[self findImageColors:imageColors backgroundColor:backgroundColor]];
while (finalColors.count < self.count) {
if (darkBackground)
[finalColors addObject:[UIColor whiteColor]];
else
[finalColors addObject:[UIColor blackColor]];
}

NSMutableArray *finalColors = [NSMutableArray array];
[finalColors addObjectsFromArray:[self findColorsOfImage:image imageColors:&imageColors]];
while (finalColors.count < self.count)
[finalColors addObject:[UIColor whiteColor]];
self.colors = [NSArray arrayWithArray:finalColors];
}

- (NSArray *)findImageColors:(NSCountedSet *)imageColors backgroundColor:(UIColor *)backgroundColor {
NSEnumerator *enumerator = [imageColors objectEnumerator];
UIColor *curColor = nil;
NSMutableArray *sortedColors = [NSMutableArray arrayWithCapacity:imageColors.count];
NSMutableArray *resultColors = [NSMutableArray array];
BOOL findDarkTextColor = ![backgroundColor isDarkColor];

while ((curColor = [enumerator nextObject]) != nil) {
curColor = [curColor colorWithMinimumSaturation:0.15f];
if ([curColor isDarkColor] == findDarkTextColor) {
NSUInteger colorCount = [imageColors countForObject:curColor];
[sortedColors addObject:[[TDCountedColor alloc] initWithColor:curColor count:colorCount]];
}
}

[sortedColors sortUsingSelector:@selector(compare:)];

for (TDCountedColor *countedColor in sortedColors) {
curColor = countedColor.color;
BOOL continueFlag = NO;
for (UIColor *c in resultColors) {
if (![curColor isDistinct:c]) {
continueFlag = YES;
break;
}
}
if (continueFlag)
continue;
if (resultColors.count < self.count - 1)
[resultColors addObject:curColor];
else
break;
}
return [NSArray arrayWithArray:resultColors];
}

- (UIColor *)findBackroundColorOfImage:(UIImage *)image imageColors:(NSCountedSet **)colors {
- (NSArray *)findColorsOfImage:(UIImage *)image imageColors:(NSCountedSet **)colors {
size_t width = CGImageGetWidth(image.CGImage);
size_t height = CGImageGetHeight(image.CGImage);

NSCountedSet *imageColors = [[NSCountedSet alloc] initWithCapacity:width * height];
NSCountedSet *leftEdgeColors = [[NSCountedSet alloc] initWithCapacity:height];

NSDate *start = [NSDate date];
for (NSUInteger x = 0; x < width; x++) {
for (NSUInteger y = 0; y < height; y++) {
UIColor *color = [UIImage colorFromImage:image atX:x andY:y];
if (x == 0)
[leftEdgeColors addObject:color];
[imageColors addObject:color];
}
}
NSDate *finish = [NSDate date];
NSTimeInterval execution = [finish timeIntervalSinceDate:start];

*colors = imageColors;

NSEnumerator *enumerator = [leftEdgeColors objectEnumerator];
NSEnumerator *enumerator = [imageColors objectEnumerator];
UIColor *curColor = nil;
NSMutableArray *sortedColors = [NSMutableArray arrayWithCapacity:leftEdgeColors.count];
NSMutableArray *sortedColors = [NSMutableArray arrayWithCapacity:imageColors.count];
NSMutableArray *resultColors = [NSMutableArray array];

while ((curColor = [enumerator nextObject]) != nil) {
NSUInteger colorCount = [leftEdgeColors countForObject:curColor];
NSInteger randomColorsThreshold = (NSInteger)(height * kColorThresholdMinimumPercentage);
if (colorCount < randomColorsThreshold)
continue;
curColor = [curColor colorWithMinimumSaturation:0.15f];
NSUInteger colorCount = [imageColors countForObject:curColor];
[sortedColors addObject:[[TDCountedColor alloc] initWithColor:curColor count:colorCount]];
}

[sortedColors sortUsingSelector:@selector(compare:)];

TDCountedColor *proposedBackgroundColor = nil;

if (sortedColors.count > 0) {
proposedBackgroundColor = sortedColors[0];
if ([proposedBackgroundColor.color isBlackOrWhite]) {
for (NSInteger i = 1; i < sortedColors.count; i++) {
TDCountedColor *nextProposed = sortedColors[i];
if (((double)nextProposed.count / (double)proposedBackgroundColor.count) > 0.3f) {
if (![nextProposed.color isBlackOrWhite]) {
proposedBackgroundColor = nextProposed;
break;
}
} else {
break;
}
for (TDCountedColor *countedColor in sortedColors) {
curColor = countedColor.color;
BOOL continueFlag = NO;
for (UIColor *c in resultColors) {
if (![curColor isDistinct:c]) {
continueFlag = YES;
break;
}
}
if (continueFlag)
continue;
if (resultColors.count < self.count)
[resultColors addObject:curColor];
else
break;
}

return proposedBackgroundColor.color;
return [NSArray arrayWithArray:resultColors];
}

@end
Expand Down

0 comments on commit 09b712f

Please sign in to comment.