iOS 获取一张图片的主题色(分别用swift和OC实现)

6 篇文章 0 订阅

需求背景

  • 有时候我们会有这样的需求,用户从相册选择一张照片,返回展示的时候,除了展示照片还要让整体背景也是和照片相近颜色,最近自己写了一个图片加水印的项目,分别用swift和OC实现相关功能。

代码部分

  • 主要逻辑:

  1. 将图片按比例缩小,因为后续遍历图片每个像素点,循环次数是图片width x height,如果直接原图去遍历,可能一次循环就要跑几十万、百万次,需要时间非常久,所以要将图片缩小。

  2. 获取图片的所有像素的RGB值,每组RGB使用数组存储(可以根据自己的需求过滤部分颜色),然后用Set将数组装起来。

  3. 统计Set里面相同次数最多的色值,即是整个图片的主题色

swift代码实现 

public extension UIImage {
    func subjectColor(_ completion: @escaping (_ color: UIColor?) -> Void){
      DispatchQueue.global().async {
        if self.cgImage == nil {
            DispatchQueue.main.async {
                return completion(nil)
            }
        }
        let bitmapInfo = CGBitmapInfo(rawValue: 0).rawValue | CGImageAlphaInfo.premultipliedLast.rawValue
          
        // 第一步 先把图片缩小 加快计算速度. 但越小结果误差可能越大
        let thumbSize = CGSize(width: 40 , height: 40)
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        guard let context = CGContext(data: nil,
                                      width: Int(thumbSize.width),
                                      height: Int(thumbSize.height),
                                      bitsPerComponent: 8,
                                      bytesPerRow: Int(thumbSize.width) * 4 ,
                                      space: colorSpace,
                                      bitmapInfo: bitmapInfo) else { return completion(nil) }
          
        let drawRect = CGRect(x: 0, y: 0, width: thumbSize.width, height: thumbSize.height)
        context.draw(self.cgImage! , in: drawRect)
          
        // 第二步 取每个点的像素值
        if context.data == nil{ return completion(nil)}
        let countedSet = NSCountedSet(capacity: Int(thumbSize.width * thumbSize.height))
        for x in 0 ..< Int(thumbSize.width) {
            for y in 0 ..< Int(thumbSize.height){
                let offset = 4 * x * y
                let red = context.data!.load(fromByteOffset: offset, as: UInt8.self)
                let green = context.data!.load(fromByteOffset: offset + 1, as: UInt8.self)
                let blue = context.data!.load(fromByteOffset: offset + 2, as: UInt8.self)
                let alpha = context.data!.load(fromByteOffset: offset + 3, as: UInt8.self)
                // 过滤透明的、基本白色、基本黑色
                if alpha > 0 && (red < 250 && green < 250 && blue < 250) && (red > 5 && green > 5 && blue > 5) {
                    let array = [red,green,blue,alpha]
                    countedSet.add(array)
                }
            }
        }
          
        //第三步 找到出现次数最多的那个颜色
        let enumerator = countedSet.objectEnumerator()
        var maxColor: [Int] = []
        var maxCount = 0
        while let curColor = enumerator.nextObject() as? [Int] , !curColor.isEmpty {
            let tmpCount = countedSet.count(for: curColor)
            if tmpCount < maxCount { continue }
            maxCount = tmpCount
            maxColor = curColor
        }
        let color = UIColor(red: CGFloat(maxColor[0]) / 255.0, green: CGFloat(maxColor[1]) / 255.0, blue: CGFloat(maxColor[2]) / 255.0, alpha: CGFloat(maxColor[3]) / 255.0)
        DispatchQueue.main.async { return completion(color) }
      }
   }
}

OC代码实现 

//根据图片获取图片的主色调
-(void)getSubjectColor:(void(^)(UIColor*))callBack {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 第一步 先把图片缩小 加快计算速度. 但越小结果误差可能越大
        int bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
        CGSize thumbSize = CGSizeMake(40, 40);
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGContextRef context = CGBitmapContextCreate(NULL,thumbSize.width,thumbSize.height, 8, thumbSize.width*4, colorSpace,bitmapInfo);
        CGRect drawRect = CGRectMake(0, 0, thumbSize.width, thumbSize.height);
        CGContextDrawImage(context, drawRect, self.CGImage);
        CGColorSpaceRelease(colorSpace);
        
        // 第二步 取每个点的像素值
        unsigned char* data = CGBitmapContextGetData (context);
        if (data == NULL) {
            dispatch_async(dispatch_get_main_queue(), ^{
                callBack(nil);
            });
        };
        NSCountedSet* cls = [NSCountedSet setWithCapacity: thumbSize.width * thumbSize.height];
        for (int x = 0; x < thumbSize.width; x++) {
            for (int y = 0; y < thumbSize.height; y++) {
                int offset = 4 * (x * y);
                int red = data[offset];
                int green = data[offset + 1];
                int blue = data[offset + 2];
                int alpha =  data[offset + 3];
                // 过滤透明的、基本白色、基本黑色
                if (alpha > 0 && (red < 250 && green < 250 && blue < 250) && (red > 5 && green > 5 && blue > 5)) {
                    NSArray *clr = @[@(red),@(green),@(blue),@(alpha)];
                    [cls addObject:clr];
                }
            }
        }
        CGContextRelease(context);
        
        //第三步 找到出现次数最多的那个颜色
        NSEnumerator *enumerator = [cls objectEnumerator];
        NSArray *curColor = nil;
        NSArray *MaxColor = nil;
        NSUInteger MaxCount = 0;
        while ((curColor = [enumerator nextObject]) != nil){
            NSUInteger tmpCount = [cls countForObject:curColor];
            if ( tmpCount < MaxCount ) continue;
            MaxCount = tmpCount;
            MaxColor = curColor;
        }
        UIColor * subjectColor = [UIColor colorWithRed:([MaxColor[0] intValue]/255.0f) green:([MaxColor[1] intValue]/255.0f) blue:([MaxColor[2] intValue]/255.0f) alpha:([MaxColor[3] intValue]/255.0f)];
        dispatch_async(dispatch_get_main_queue(), ^{
            callBack(subjectColor);
        });
    });
}

因为里面是两个for循环,时间复杂度是On^2,如果设置的width和Height比较大的话,会比较耗时,在主线程里面执行可能会卡住,所以使用了gcd开启子线程去执行,完成后回到主线程执行回调。 

外部使用:

@objc func buttonClick(){
        /// 4. 点击选图片时,展示这个 picker controller
        present(imagePicker, animated: true) {
            print("UIImagePickerController: presented")
        }
    }
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        guard let selectedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
        picker.dismiss(animated: true) { [unowned self] in
            self.imageView.image = selectedImage
            
            // Swift
            selectedImage.subjectColor({[unowned self] color in
                guard let subjectColor = color else { return }
                    self.view.backgroundColor = subjectColor
            })

            // OC
//            selectedImage.getSubjectColor {[unowned self] color in
//                guard let subjectColor = color else { return }
//                self.view.backgroundColor = subjectColor
//            }
        }
    }

 

转载自:iOS 获取图片的主题色 - 掘金 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值