java 求图片主色 简单版

原因:

其他的工具类求图片主色 一会儿边缘梯度啊,一会儿HSL,一会儿HSV啊,脑袋大.
就自己搞了个工具类.

原理:

你需要了解颜色立方体

在这里插入图片描述
原理如下:
1.将RGB颜色立方体 切割为125 个小正方体.
2.将图片的颜色数据 扔到 125个小正方体里面
3.最后统计出哪些小正方体里面最多,然后排序取前8个正方体的中心点,就得出了图片的主色

以上就是原理,但是里面还是包括了一些细节,比如说:

  • 图片数据太大,肯定要吃很多内存,这里先做了图片缩略处理.
  • 分割立方体方式,肯定不完全准确,但是大部分情况下,不仔细对比,一般肉眼看不出.
  • 如果一个图片是个纯色,但该颜色处于小正方体的边缘,那么最终求出来的却是小正方体的中心点,那么是可以看出区别的.
  • 由于白色正方体 的中心点是灰白色的, 和白色 肉眼查看是有明显区别的,所以在代码里面,额外添加了一个白色正方体,一共125 + 1 = 126个白色正方体供接受图片颜色数据

依赖

    <dependency>
        <groupId>net.coobird</groupId>
        <artifactId>thumbnailator</artifactId>
        <version>0.4.8</version>
    </dependency>

工具类



import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 获取图片主色的工具类
 */
@Slf4j
public class ImgColorUtil {

    //将色系分成5格
    public static  final int SPLIT_COUNT = 5;
    //取结果的前8个
    public static  final int RES_MAX_COUNT = 8;
    //图片压缩比例
    public static  final float IMG_SCALE = 0.2f;

    public static  RgbInfoBuilder rgbBuilder = RgbInfoBuilder.buildSplit(SPLIT_COUNT);

    public static void main(String[] args) throws IOException {
        List<String> colors = getColors(new File("D:\\aaa\\aaa.jpg"));
        colors.forEach(System.out::println);


        RgbInfo containsInfo = rgbBuilder.getContainsInfo("#FFFFF1");
        System.out.println(containsInfo);


    }

    /**
     * 通过图片file获取主色
     * @param file
     * @return
     * @throws IOException
     */
    public static List<String> getColors(File file) throws IOException {
        return getColors(new FileInputStream(file));
    }

    /**
     * 通过文件流获取主色
     * @param inputStream
     * @return
     */
    public static List<String> getColors(InputStream inputStream){

        List<Rgb> rgbs = null;
        try {
            rgbs = getRgbs(inputStream);
            List<RgbInfoCount> resolving = rgbBuilder.resolving(rgbs);
            return resolving.subList(0, RES_MAX_COUNT).stream().map(s -> s.getInfo().getCenter()).collect(Collectors.toList());
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 将图片字节 转换成rgb对象数组
     * @param inputStream
     * @return
     * @throws IOException
     */
    private static List<Rgb> getRgbs(InputStream inputStream) throws IOException {
        //将图片压缩,降低图片质量,减少图片字节数量,否则可能会溢出。
        BufferedImage bufferedImage = Thumbnails.of(inputStream)
                .scale(IMG_SCALE).asBufferedImage();

        //获取压缩后的图片的所有rgb字符串比如“FF00FF”放到一个list:rgbStrs里面
        int height = bufferedImage.getHeight();
        int width = bufferedImage.getWidth();
        List<String> rgbStrs = new ArrayList<>();
        for (int i =0 ; i < width ;i ++){
            for (int j =0;j<height;j++){
                int rgb = bufferedImage.getRGB(i, j);
                try{
                    //为什么substring(2)
                    //因为字节是00FF00FF,首个00 是透明度,剩下3个才是rgb
                    rgbStrs.add(Integer.toHexString(rgb).substring(2));
                }catch (Exception e){
                    log.error("加入rgb数据错误:{},{},{}",i,j,Integer.toHexString(rgb));
                }

            }
        }

        // 封装成rgb的对象组装list并返回
        List<Rgb> rgbs = new ArrayList<>();
        for(String rgbStr:rgbStrs){
            rgbs.add(getRgb10(rgbStr));
        }
        return rgbs;
    }

    /**
     *  字符串16进制rgb 转换成10 进制的对象
     * @param rgb16 FF00FF
     * @return
     */
    public static Rgb getRgb10(String rgb16){
        rgb16 = rgb16.trim().replace("#","");

        String rstring = rgb16.substring(0, 2);
        String gstring = rgb16.substring(2, 4);
        String bstring = rgb16.substring(4);

        return new Rgb(Integer.parseInt(rstring,16),Integer.parseInt(gstring,16)
                ,Integer.parseInt(bstring,16));

    }


    /**
     * RGB 对象
     */
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Rgb{
        int r;
        int g;
        int b;
    }
    @Data
    @AllArgsConstructor
    static class RgbInfoCount{
        RgbInfo info;
        int count;
    }

    /**
     * 颜色正方体 类
     */
    @Data
    @EqualsAndHashCode
    public static class RgbInfo{
        //额外的白色正方体
        public static RgbInfo WHITE = new RgbInfo(250,250,250,10);

        //正方体 前边界
        int preRBorder;
        int preGBorder;
        int preBBorder;
        // 正方体 中心点
        int centerR;
        int centerG;
        int centerB;
        // 正方体后边界
        int afterRBorder;
        int afterGBorder;
        int afterBBorder;
        // 正方体中心点的 16进制字符串
        String center;

        //根据前边界 和 正方体边长,获取一个正方体
        public RgbInfo(int preRBorder, int preGBorder, int preBBorder,int len) {


            this.preRBorder = preRBorder;
            this.preGBorder = preGBorder;
            this.preBBorder = preBBorder;

            this.afterRBorder = preRBorder + len;
            this.afterGBorder = preGBorder + len;
            this.afterBBorder = preBBorder + len;

            this.centerR = preRBorder + len/2;
            this.centerG = preGBorder + len/2;
            this.centerB = preBBorder + len/2;


            this.center ="#"+ Integer.toHexString(centerR) + Integer.toHexString(centerG)+Integer.toHexString(centerB);
        }
    }

    /**
     * 正方体处理类
     */
    @Data
    public static class RgbInfoBuilder{
        // 颜色rgb表示的最大值,两个16进制 ,比如 FF = 255,包括0
        private static  final int MAXSIZE = 255;
        // 切割后的颜色正方体
        private List<RgbInfo> infos;

        /**
         * 切割正方体 初始化
         * @param size 如果size 是5 ,那么 会切割成5*5*5=125块
         * @return
         */
        public static RgbInfoBuilder  buildSplit(int size){
            if (MAXSIZE% size != 0){
                throw new RuntimeException("size必须能整除于"+MAXSIZE);
            }
            List<RgbInfo> infos = new ArrayList<>();
            int len = MAXSIZE/size;
            for (int r = 0;r < size ;r++){
                for (int g = 0;g < size ;g++){
                    for (int b = 0;b < size ;b++){
                        RgbInfo rgbInfo = new RgbInfo(r * len, g * len, b * len, len);
                        infos.add(rgbInfo);
                    }
                }
            }
            RgbInfoBuilder rgbInfoBuilder = new RgbInfoBuilder();
            rgbInfoBuilder.setInfos(infos);
            return rgbInfoBuilder;
        }

        /**
         *  根据 传递来的rgb信息,将rgb 信息放入小颜色正方体 中,然后计算 命中的 小颜色正方体及个数
         * @param rgbs
         * @return
         */
        public List<RgbInfoCount> resolving(List<Rgb> rgbs){
            Map<RgbInfo,Integer> infoCounts = new HashMap<>();
            for (Rgb rgb:rgbs){
                RgbInfo info = getContainsInfo(rgb.getR(), rgb.getG(), rgb.getB());
                if (infoCounts.containsKey(info)){
                    infoCounts.put(info,infoCounts.get(info)+1);
                }else {
                    infoCounts.put(info,1);
                }
            }
            List<RgbInfoCount> list = new ArrayList<>();
            infoCounts.forEach((k,v) ->{
                list.add(new RgbInfoCount(k,v));
            });
            list.sort((o1,o2) ->{
                return o2.getCount()-o1.getCount();
            });
            return list;
        }

        /**
         *  根据rgb信息,得出命中 哪个小正方体,注意,额外添加一个白色正方体.
         * @param r
         * @param g
         * @param b
         * @return
         */
        public RgbInfo getContainsInfo(int r, int g, int b){
            boolean white = isContains(r, g, b, RgbInfo.WHITE);
            if (white){
                return RgbInfo.WHITE;
            }
            for(RgbInfo info : infos){
                boolean contains = isContains(r, g, b, info);
                if (contains) return info;
            }
            return null;
        }

        /**
         * 根据rgb信息字符串,得出命中 哪个小正方体,注意,额外添加一个白色正方体.
         * @param rgb
         * @return
         */
        public RgbInfo getContainsInfo(String rgb){
            Rgb rgb10 = getRgb10(rgb);
            return getContainsInfo(rgb10.getR(),rgb10.getG(),rgb10.getB());
        }

        /**
         *  如何命中小正方体
         * @param r
         * @param g
         * @param b
         * @param info
         * @return
         */
        private boolean isContains(int r, int g, int b, RgbInfo info) {
            if (r >= info.getPreRBorder()
                    && r <= info.getAfterRBorder()
                    && g >= info.getPreGBorder()
                    && g <= info.getAfterGBorder()
                    && b >= info.getPreBBorder()
                    && b <= info.getAfterBBorder()){
                return true;
            }
            return false;
        }


    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值