java处理图片,按比率缩放和高斯模糊算法

1,先上案例:

 图1是原图。图2是处理后的。

2,处理主类:

public class ImgHandlerUtil {

    private int width = 702;
    private int height = 280;
    private float rate = width / (float) height;
    private float allow = 0.2f;

    //需要处理的img缓存对象
    private BufferedImage bImg;

    //新建图片缓存
    private BufferedImage cacheImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    //画布
    private Graphics2D graphics;

    /**
     *
     * @param width 要求的宽
     * @param height 要求的高
     * @param allow 允许宽/高的比率在多少范围内,比率不大于0.5,越小图片处理越好。
     * @param img 需要处理的图片
     */
    public ImgHandlerUtil(int width, int height, float allow, BufferedImage img) {
        if (width <= 0)throw new RuntimeException("宽必须大于等于0");
        if (height <= 0)throw new RuntimeException("高必须大于登录0");
        if (img == null)throw new RuntimeException("图片不能为空");
        this.width = width;
        this.height = height;
        this.allow = allow;
        this.bImg = img;
        graphics = cacheImg.createGraphics();
    }

    //测试
//    public static void main(String[] args) throws IOException {
//        BufferedImage read = ImageIO.read(new File("C:\\555.jpg"));
//        ImgHandlerUtil imgHandlerUtil = new ImgHandlerUtil(702, 280, 0.2f, read);
//        BufferedImage build = imgHandlerUtil.fillBackgroundImg().fuzzy().ratioShowImg(true).build();
//        ImageIO.write(build, "jpg", new File("E:\\360downloads\\4566.jpg"));
//    }

    /**
     * 构建图片
     *
     * @return
     */
    public BufferedImage build() {
        graphics.dispose();
        bImg = null;
        return cacheImg;
    }

    /**
     * 使用“高斯”算法,把图片模糊。
     * @return 返回当前对象做其他操作
     */
    public ImgHandlerUtil fuzzy() {
        int pixel = 0;
        int[][][] picture = new int[3][width][height];
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                int rgb = cacheImg.getRGB(x, y);
                //获取每个点的RGB值
                picture[0][x][y] = (rgb & 0xff0000) >> 16;
                picture[1][x][y] = (rgb & 0xff00) >> 8;
                picture[2][x][y] = (rgb & 0xff);
            }
        }
        picture = GaussianBlur.gaussianBlur(picture, 100, 20);  // 快速高斯模糊
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                pixel = ((picture[0][i][j] & 0xff) << 16) + ((picture[1][i][j] & 0xff) << 8) + (picture[2][i][j] & 0xff);
                cacheImg.setRGB(i, j, pixel);
            }
        }
        return this;
    }

    /**
     * 图片按比率缩放,在规定的大小中进行全部展示
     * @param isCenter 展示的图片是否在居中展示。上下左右都会居中展示
     * @return 返回当前对象
     */
    public ImgHandlerUtil ratioShowImg(boolean isCenter) {
        int x = 0;
        int y = 0;
        PairValue<Integer, Integer> pairValue = calcAdaptSize();
        Image scaledInstance = bImg.getScaledInstance(pairValue.getLeft(), pairValue.getRight(), Image.SCALE_DEFAULT);
        if (isCenter) {
            PairValue<Integer, Integer> center = center(pairValue.getLeft(), pairValue.getRight());
            x = center.getLeft();
            y = center.getRight();
        }

        graphics.drawImage(scaledInstance, x, y, null);
        return this;
    }

    /**
     * 填充背景图片,使整个图片填充完规定的尺寸。
     * @return 返回当前对象,做其他处理
     * @throws IOException
     */
    public ImgHandlerUtil fillBackgroundImg() {
        PairValue<Integer, Integer> pairValue = calcFillSizeV1();
        Image scaledInstance = bImg.getScaledInstance(pairValue.getLeft(), pairValue.getRight(), Image.SCALE_DEFAULT);
        graphics.drawImage(scaledInstance, 0, 0, null);
        return this;
    }

    /**
     * 老旧的写法,效率慢
     * 计算图片要填满一个宽高多少的矩形,会有两种情况:
     * 1,图片的宽高大于要求的宽高。
     * 2,图片的宽高小于要求的宽高。
     */
    @Deprecated
    public PairValue<Integer, Integer> calcFillSize() {
        int height1 = bImg.getHeight();
        int width1 = bImg.getWidth();
        int rate = getRate();
        if (rate == 0) return new PairValue<>(height1, width1);

        boolean greaterThan = isGreaterThan(true);
        int i = 0;
        if (greaterThan) {
            if (rate == 1) {
                i = bImg.getHeight() - height;
            } else if (rate == 2) {
                i = bImg.getWidth() - width;
            }
            int newH = bImg.getHeight() - i;
            int newW = bImg.getWidth() - i;
            return new PairValue<>(newW, newH);
        }

        if (rate == 1) {
            i = height - bImg.getHeight();
        } else if (rate == 2) {
            i = width - bImg.getWidth();
        }
        int newH = bImg.getHeight() + i;
        int newW = bImg.getWidth() + i;
        return new PairValue<>(newW, newH);
    }

    /**
     * 计算图片,如果图片宽度跟高度不够,按比率放大图片来填充规定的大小。多余部分不进行展示。
     * @return 返回按比率缩放后的图片尺寸
     */
    public PairValue<Integer, Integer> calcFillSizeV1() {
        int height = bImg.getHeight();
        int width = bImg.getWidth();

        int rate = getRate();
        if (rate == 0)return new PairValue<>(this.width, this.height);

        int h, w;
        if (rate == 1) { //同时与高为缩进比例
            h = this.height;
            float rh = height / (float) this.height; //比例高
            w = Math.round(width / rh);  //宽除以比例获取缩进的比例宽
        } else { //同时与宽为缩进比例
            w = this.width;
            float rw = width / (float) this.width;
            h = Math.round(height / rw);
        }
        return new PairValue<>(w, h);
    }

    /**
     * 如果宽跟高的比率不正比。
     * 如果在允许的条件内返回0,宽的比率大于高返回1,则高的比率大于宽返回2
     *
     * @return
     */
    public int getRate() {
        int height = bImg.getHeight();
        int width = bImg.getWidth();
        float v = width / (float) height;
        if (v <= allow && isGreaterThan(false)) return 0;
        if (v > rate) return 1;
        return 2;
    }

    /**
     * true表示and,false表示or
     *
     * @param andOr 如果是and,就表示宽度都大于指定尺寸就返回true,否则返回false。如果or,只要宽或者高一个大于指定宽高就返回true,否则返回false。
     * @return
     */
    public boolean isGreaterThan(boolean andOr) {
        int height = bImg.getHeight();
        int width = bImg.getWidth();
        return andOr ? (height > this.height && width > this.width) : (height > this.height || width > this.width);
    }

    /**
     * 把图片按比率等比缩放,适应要求的宽高。
     * @return 返回按比率缩放后的图片尺寸
     */
    public PairValue<Integer, Integer> calcAdaptSize() {
        int height = bImg.getHeight();
        int width = bImg.getWidth();

        int rate = getRate();
        if (rate == 0)return new PairValue<>(this.width, this.height);

        int h, w;
        if (rate == 2) { //同时与高为缩进比例
            h = this.height;
            float rh = height / (float) this.height; //比例高
            w = Math.round(width / rh);  //宽除以比例获取缩进的比例宽
        } else { //同时与宽为缩进比例
            w = this.width;
            float rw = width / (float) this.width;
            h = Math.round(height / rw);
        }
        return new PairValue<>(w, h);
    }


    /**
     * 计算图片居中起始坐标
     *
     * @param width  图片的宽度
     * @param height 图片的高度
     * @return
     */
    public PairValue<Integer, Integer> center(int width, int height) {
        int y = (this.height - height) / 2;
        int x = (this.width - width) / 2;
        return new PairValue<>(x, y);
    }
}

3,优雅返回两个值

package com.ygyang.utils;

public class PairValue <L,R>{
   private L left;
   private R right;

    public PairValue(L left, R right) {
        this.left = left;
        this.right = right;
    }

    public L getLeft() {
        return left;
    }

    public void setLeft(L left) {
        this.left = left;
    }

    public R getRight() {
        return right;
    }

    public void setRight(R right) {
        this.right = right;
    }
}

4,高斯模糊算法

package com.ygyang.img.utils;

/**
 * 高斯模糊算法
 */
public class GaussianBlur {
        private static final int precision = 10000; // 精度,由于返回的是int数组,精度较低,因此需要将所有值同时扩大数倍,可以理解为把小数点向右移动
        private static final double E = 2.718281828459045;//自然常数e
        private static final double PI = 3.141592653589793;//圆周率

        /**
         * 快速高斯模糊
         * @param picture 三维数组,picture[a][b][c],a表示颜色,012分别为R,G,B;b和c代表尺寸,宽度为b,高度为c
         * @param radius 半径
         * @param SIGMA 值越大越模糊
         * @return 格式如同picture的数组
         */
        public static int[][][] gaussianBlur(int[][][] picture,int radius,double SIGMA){
            int i, j, x, R, G, B, proportion, subscript;
            int[] matrix = linearNormalDistribution(radius,SIGMA);
            int width = picture[0].length, height = picture[0][0].length;
            int[][][] color_1 = new int[3][width][height]; // 用来存高斯模糊后的数据
            int[][][] color_2 = new int[3][width][height]; // 临时存储纵向滤波之后的数据
            //纵向滤波
            for (i = 0; i < width; i++) {
                for (j = 0; j < height; j++) {
                    R = G = B = 0;
                    for (x = j - radius; x <= j + radius; x++) {
                        proportion = matrix[x + radius - j];
                        subscript = (x >= 0 && x < height) ? x : 2 * j - x; // 如果坐标越界了,则计算对称点来代替
                        R += picture[0][i][subscript] * proportion;
                        G += picture[1][i][subscript] * proportion;
                        B += picture[2][i][subscript] * proportion;
                    }
                    color_2[0][i][j] = R / precision;
                    color_2[1][i][j] = G / precision;
                    color_2[2][i][j] = B / precision;
                }
            }
            //横向滤波
            for (i = 0; i < height; i++) {
                for (j = 0; j < width; j++) {
                    R = G = B = 0;
                    for (x = j - radius; x <= j + radius; x++) {
                        proportion = matrix[x + radius - j];
                        subscript = (x >= 0 && x < width) ? x : 2 * j - x;
                        R += color_2[0][subscript][i] * proportion;
                        G += color_2[1][subscript][i] * proportion;
                        B += color_2[2][subscript][i] * proportion;
                    }
                    //注意for语句中i代表高度,j代表宽度,所以下面三个语句的i和j并没有写错位置
                    color_1[0][j][i] = R / precision;
                    color_1[1][j][i] = G / precision;
                    color_1[2][j][i] = B / precision;
                }
            }
            return color_1;
        }

        /**
         * 慢速高斯模糊,采用二维正态分布的方法来处理图像
         * @param picture 三维数组,picture[a][b][c],a表示颜色,012分别为R,G,B;b和c代表尺寸,宽度为b,高度为c
         * @param radius 半径
         * @return 格式如同picture的数组
         */
        public static int[][][] slowGaussianBlur(int[][][] picture,int radius){
            //flag为真时计算加权,为假时直接代入矩阵
            int[][] matrix = normalDistribution(radius,1.5);
            int i, j, x, y, R, G, B, proportion, left, right, width = picture[0].length, height = picture[0][0].length;
            int[][][] color = new int[3][width][height];
            //选取每个点
            for (i = 0; i < width; i++) {
                for (j = 0; j < height; j++) {
                    //选取半径为radius的矩阵
                    R = G = B = 0;
                    for (x = i - radius; x <= i + radius; x++) {
                        for (y = j - radius; y <= j + radius; y++) {
                            //求出颜色
                            proportion = matrix[x + radius - i][y + radius - j];
                            left = (x >= 0 && x < width) ? x : 2 * i - x;
                            right = (y >= 0 && y < height) ? y : 2 * j - y;
                            R += picture[0][left][right] * proportion;
                            G += picture[1][left][right] * proportion;
                            B += picture[2][left][right] * proportion;
                        }
                    }
                    color[0][i][j] = R / precision;
                    color[1][i][j] = G / precision;
                    color[2][i][j] = B / precision;
                }
            }
            return color;
        }

        /**
         * 用一维正态分布函数来计算“权值列向量”,效率较高
         * @param radius 模糊半径
         * @param SIGMA 正态分布参数,如果自己没把握,就填1.5
         * @return “权值列向量”
         */
        private static int[] linearNormalDistribution(int radius,double SIGMA){
            int[] matrix = new int[2 * radius + 1]; // 定义一个列向量
            int sum, i;
            //计算各个点的正态分布值
            sum = matrix[radius] = (int) (precision / (2 * PI * SIGMA * SIGMA)); // sum的初值为向量中心点的值,例如向量(1,2,3,2,1),则初值为3
            for (i = 1; i <= radius; i++) {
                //根据对称性,可以减少一倍的运算量,i=0的情况已经在sum初值那一步考虑
                matrix[radius-i] = matrix[radius+i] = (int) ((Math.pow(E, -i * i / (2 * SIGMA * SIGMA)) / (2 * PI * SIGMA * SIGMA)) * precision);
                sum += matrix[radius+i] * 2; // 计算向量所有值之和
            }
            for (i = 0; i < 2 * radius + 1; i++) {
                matrix[i] = matrix[i] * precision / sum; // 所有值都除以sum,确保它们的和为“1”,由于扩大了10000倍,所以这个“1”实际上应该是10000
            }
            return matrix;
        }

        /**
         * 用二维正态分布函数来计算权值矩阵,效率较低
         * @param radius 模糊半径
         * @param SIGMA 正态分布参数,如果自己没把握,就填1.5
         * @return 权值矩阵
         */
        private static int[][] normalDistribution(int radius, double SIGMA) {
            int sum = 0, i, j;
            int[][] matrix = new int[2 * radius + 1][2 * radius + 1]; // 定义一个矩阵
            //计算各个点的正态分布值
            for (i = 0; i <= radius; i++) {
                for (j = 0; j <= radius; j++) {
                    //写入矩阵并累加,根据矩阵的对称性可以减少3/4的运算量
                    matrix[radius-i][radius-j]
                            = matrix[radius-i][radius+j]
                            = matrix[radius+i][radius-j]
                            = matrix[radius+i][radius+j]
                            = (int) (Math.pow(E, -(i * i + j * j) / (2 * SIGMA * SIGMA)) / (2 * PI * SIGMA * SIGMA) * precision);
                    sum += 4 * matrix[radius+i][radius+j];
                }
            }
            //计算权值
            for (i = 0; i <= 2 * radius; i++) {
                for (j = 0; j <= 2 * radius; j++) {
                    matrix[i][j] = matrix[i][j] * precision / sum; // 所有值都除以sum,确保它们的和为“1”,由于扩大了10000倍,所以这个“1”实际上应该是10000
                }
            }
            return matrix;
        }
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值