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;
}
}