add methods

This commit is contained in:
Looly
2021-08-10 20:54:31 +08:00
parent 838a2c45a6
commit 8ddb2b425c
4 changed files with 90 additions and 45 deletions

View File

@@ -3,7 +3,7 @@
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
# 5.7.8 (2021-08-09) # 5.7.8 (2021-08-10)
### 🐣新特性 ### 🐣新特性
* 【core 】 MapProxy支持return this的setter方法pr#392@Gitee * 【core 】 MapProxy支持return this的setter方法pr#392@Gitee
@@ -14,6 +14,7 @@
### 🐞Bug修复 ### 🐞Bug修复
* 【core 】 改进NumberChineseFormatter算法补充完整单元测试解决零问题 * 【core 】 改进NumberChineseFormatter算法补充完整单元测试解决零问题
* 【core 】 修复Img变换操作图片格式问题issue#I44JRB@Gitee
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------

View File

@@ -27,9 +27,7 @@ import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D; import java.awt.geom.Ellipse2D;
import java.awt.geom.RoundRectangle2D; import java.awt.geom.RoundRectangle2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.CropImageFilter; import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource; import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter; import java.awt.image.ImageFilter;
@@ -230,19 +228,19 @@ public class Img implements Serializable {
// 自动修正负数 // 自动修正负数
scale = -scale; scale = -scale;
} }
final Image srcImg = getValidSrcImg(); final Image srcImg = getValidSrcImg();
// PNG图片特殊处理 // PNG图片特殊处理
if (ImgUtil.IMAGE_TYPE_PNG.equals(this.targetImageType)) { if (ImgUtil.IMAGE_TYPE_PNG.equals(this.targetImageType)) {
final AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(scale, scale), null); // 修正float转double导致的精度丢失
this.targetImage = op.filter(ImgUtil.toBufferedImage(srcImg), null); final double scaleDouble = NumberUtil.toDouble(scale);
this.targetImage = ImgUtil.transform(AffineTransform.getScaleInstance(scaleDouble, scaleDouble),
ImgUtil.toBufferedImage(srcImg, this.targetImageType));
} else { } else {
final String scaleStr = Float.toString(scale);
// 缩放后的图片宽 // 缩放后的图片宽
int width = NumberUtil.mul(Integer.toString(srcImg.getWidth(null)), scaleStr).intValue(); final int width = NumberUtil.mul((Number) srcImg.getWidth(null), scale).intValue();
// 缩放后的图片高 // 缩放后的图片高
int height = NumberUtil.mul(Integer.toString(srcImg.getHeight(null)), scaleStr).intValue(); final int height = NumberUtil.mul((Number) srcImg.getHeight(null), scale).intValue();
scale(width, height); scale(width, height);
} }
return this; return this;
@@ -277,8 +275,8 @@ public class Img implements Serializable {
double sy = NumberUtil.div(height, srcHeight); double sy = NumberUtil.div(height, srcHeight);
if (ImgUtil.IMAGE_TYPE_PNG.equals(this.targetImageType)) { if (ImgUtil.IMAGE_TYPE_PNG.equals(this.targetImageType)) {
final AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(sx, sy), null); this.targetImage = ImgUtil.transform(AffineTransform.getScaleInstance(sx, sy),
this.targetImage = op.filter(ImgUtil.toBufferedImage(srcImg), null); ImgUtil.toBufferedImage(srcImg, this.targetImageType));
} else { } else {
this.targetImage = srcImg.getScaledInstance(width, height, scaleType); this.targetImage = srcImg.getScaledInstance(width, height, scaleType);
} }
@@ -347,8 +345,7 @@ public class Img implements Serializable {
fixRectangle(rectangle, srcImage.getWidth(null), srcImage.getHeight(null)); fixRectangle(rectangle, srcImage.getWidth(null), srcImage.getHeight(null));
final ImageFilter cropFilter = new CropImageFilter(rectangle.x, rectangle.y, rectangle.width, rectangle.height); final ImageFilter cropFilter = new CropImageFilter(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
final Image image = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(srcImage.getSource(), cropFilter)); this.targetImage = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(srcImage.getSource(), cropFilter));
this.targetImage = ImgUtil.toBufferedImage(image);
return this; return this;
} }
@@ -428,8 +425,7 @@ public class Img implements Serializable {
* @return this * @return this
*/ */
public Img gray() { public Img gray() {
final ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null); this.targetImage = ImgUtil.colorConvert(ColorSpace.getInstance(ColorSpace.CS_GRAY), getValidSrcBufferedImg());
this.targetImage = op.filter(ImgUtil.toBufferedImage(getValidSrcImg()), null);
return this; return this;
} }
@@ -456,7 +452,7 @@ public class Img implements Serializable {
* @return 处理后的图像 * @return 处理后的图像
*/ */
public Img pressText(String pressText, Color color, Font font, int x, int y, float alpha) { public Img pressText(String pressText, Color color, Font font, int x, int y, float alpha) {
final BufferedImage targetImage = ImgUtil.toBufferedImage(getValidSrcImg()); final BufferedImage targetImage = ImgUtil.toBufferedImage(getValidSrcImg(), this.targetImageType);
final Graphics2D g = targetImage.createGraphics(); final Graphics2D g = targetImage.createGraphics();
if (null == font) { if (null == font) {
@@ -477,6 +473,7 @@ public class Img implements Serializable {
new Point(x, y)); new Point(x, y));
} }
// 收笔
g.dispose(); g.dispose();
this.targetImage = targetImage; this.targetImage = targetImage;
@@ -579,7 +576,7 @@ public class Img implements Serializable {
* @since 5.4.1 * @since 5.4.1
*/ */
public Img stroke(Color color, Stroke stroke){ public Img stroke(Color color, Stroke stroke){
final BufferedImage image = ImgUtil.toBufferedImage(getValidSrcImg()); final BufferedImage image = ImgUtil.toBufferedImage(getValidSrcImg(), this.targetImageType);
int width = image.getWidth(null); int width = image.getWidth(null);
int height = image.getHeight(null); int height = image.getHeight(null);
Graphics2D g = image.createGraphics(); Graphics2D g = image.createGraphics();
@@ -717,6 +714,16 @@ public class Img implements Serializable {
return ObjectUtil.defaultIfNull(this.targetImage, this.srcImage); return ObjectUtil.defaultIfNull(this.targetImage, this.srcImage);
} }
/**
* 获取有效的源{@link BufferedImage}图片,首先检查上一次处理的结果图片,如无则使用用户传入的源图片
*
* @return 有效的源图片
* @since 5.7.8
*/
private BufferedImage getValidSrcBufferedImg() {
return ImgUtil.toBufferedImage(getValidSrcImg(), this.targetImageType);
}
/** /**
* 修正矩形框位置,如果{@link Img#setPositionBaseCentre(boolean)} 设为{@code true},则坐标修正为基于图形中心,否则基于左上角 * 修正矩形框位置,如果{@link Img#setPositionBaseCentre(boolean)} 设为{@code true},则坐标修正为基于图形中心,否则基于左上角
* *

View File

@@ -30,10 +30,14 @@ import java.awt.Image;
import java.awt.Point; import java.awt.Point;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.color.ColorSpace;
import java.awt.font.FontRenderContext; import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ColorConvertOp;
import java.awt.image.ColorModel; import java.awt.image.ColorModel;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@@ -451,9 +455,9 @@ public class ImgUtil {
} }
/** /**
* 图像切割(指定切片的行数和列数) * 图像切割(指定切片的行数和列数)默认RGB模式
* *
* @param srcImage 源图像 * @param srcImage 源图像,如果非{@link BufferedImage}则默认使用RGB模式
* @param destDir 切片目标文件夹 * @param destDir 切片目标文件夹
* @param rows 目标切片行数。默认2必须是范围 [1, 20] 之内 * @param rows 目标切片行数。默认2必须是范围 [1, 20] 之内
* @param cols 目标切片列数。默认2必须是范围 [1, 20] 之内 * @param cols 目标切片列数。默认2必须是范围 [1, 20] 之内
@@ -1167,8 +1171,8 @@ public class ImgUtil {
*/ */
public static BufferedImage toBufferedImage(Image image, String imageType) { public static BufferedImage toBufferedImage(Image image, String imageType) {
final int type = IMAGE_TYPE_PNG.equalsIgnoreCase(imageType) final int type = IMAGE_TYPE_PNG.equalsIgnoreCase(imageType)
? BufferedImage.TYPE_INT_ARGB ? BufferedImage.TYPE_INT_ARGB
: BufferedImage.TYPE_INT_RGB; : BufferedImage.TYPE_INT_RGB;
return toBufferedImage(image, type); return toBufferedImage(image, type);
} }
@@ -1652,7 +1656,7 @@ public class ImgUtil {
* @return {@link Image} * @return {@link Image}
* @since 5.5.8 * @since 5.5.8
*/ */
public static Image getImage(URL url){ public static Image getImage(URL url) {
return Toolkit.getDefaultToolkit().getImage(url); return Toolkit.getDefaultToolkit().getImage(url);
} }
@@ -2148,4 +2152,42 @@ public class ImgUtil {
public static BufferedImage backgroundRemoval(ByteArrayOutputStream outputStream, Color override, int tolerance) { public static BufferedImage backgroundRemoval(ByteArrayOutputStream outputStream, Color override, int tolerance) {
return BackgroundRemoval.backgroundRemoval(outputStream, override, tolerance); return BackgroundRemoval.backgroundRemoval(outputStream, override, tolerance);
} }
/**
* 图片颜色转换<br>
* 可以使用灰度 (gray)等
*
* @param colorSpace 颜色模式,如灰度等
* @param image 被转换的图片
* @return 转换后的图片
* @since 5.7.8
*/
public static BufferedImage colorConvert(ColorSpace colorSpace, BufferedImage image) {
return filter(new ColorConvertOp(colorSpace, null), image);
}
/**
* 转换图片<br>
* 可以使用一系列平移 (translation)、缩放 (scale)、翻转 (flip)、旋转 (rotation) 和错切 (shear) 来构造仿射变换。
*
* @param xform 2D仿射变换它执行从 2D 坐标到其他 2D 坐标的线性映射,保留了线的“直线性”和“平行性”。
* @param image 被转换的图片
* @return 转换后的图片
* @since 5.7.8
*/
public static BufferedImage transform(AffineTransform xform, BufferedImage image) {
return filter(new AffineTransformOp(xform, null), image);
}
/**
* 图片过滤转换
*
* @param op 过滤操作实现,如二维转换可传入{@link AffineTransformOp}
* @param image 原始图片
* @return 过滤后的图片
* @since 5.7.8
*/
public static BufferedImage filter(BufferedImageOp op, BufferedImage image) {
return op.filter(image, null);
}
} }

View File

@@ -10,19 +10,16 @@ import cn.hutool.extra.tokenizer.engine.mmseg.MmsegEngine;
import cn.hutool.extra.tokenizer.engine.mynlp.MynlpEngine; import cn.hutool.extra.tokenizer.engine.mynlp.MynlpEngine;
import cn.hutool.extra.tokenizer.engine.word.WordEngine; import cn.hutool.extra.tokenizer.engine.word.WordEngine;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import java.util.Iterator;
/** /**
* 模板引擎单元测试 * 模板引擎单元测试
* *
* @author looly * @author looly
* *
*/ */
public class TokenizerUtilTest { public class TokenizerUtilTest {
String text = "这两个方法的区别在于返回值"; String text = "这两个方法的区别在于返回值";
@Test @Test
@@ -32,73 +29,71 @@ public class TokenizerUtilTest {
Result result = engine.parse(text); Result result = engine.parse(text);
checkResult(result); checkResult(result);
} }
@Test @Test
public void hanlpTest() { public void hanlpTest() {
TokenizerEngine engine = new HanLPEngine(); TokenizerEngine engine = new HanLPEngine();
Result result = engine.parse(text); Result result = engine.parse(text);
String resultStr = IterUtil.join((Iterator<Word>)result, " "); String resultStr = IterUtil.join(result, " ");
Assert.assertEquals("这 两 个 方法 的 区别 在于 返回 值", resultStr); Assert.assertEquals("这 两 个 方法 的 区别 在于 返回 值", resultStr);
} }
@Test @Test
public void ikAnalyzerTest() { public void ikAnalyzerTest() {
TokenizerEngine engine = new IKAnalyzerEngine(); TokenizerEngine engine = new IKAnalyzerEngine();
Result result = engine.parse(text); Result result = engine.parse(text);
String resultStr = IterUtil.join((Iterator<Word>)result, " "); String resultStr = IterUtil.join(result, " ");
Assert.assertEquals("这两个 方法 的 区别 在于 返回值", resultStr); Assert.assertEquals("这两个 方法 的 区别 在于 返回值", resultStr);
} }
@Test @Test
public void jcsegTest() { public void jcsegTest() {
TokenizerEngine engine = new JcsegEngine(); TokenizerEngine engine = new JcsegEngine();
Result result = engine.parse(text); Result result = engine.parse(text);
checkResult(result); checkResult(result);
} }
@Test @Test
public void jiebaTest() { public void jiebaTest() {
TokenizerEngine engine = new JiebaEngine(); TokenizerEngine engine = new JiebaEngine();
Result result = engine.parse(text); Result result = engine.parse(text);
String resultStr = IterUtil.join((Iterator<Word>)result, " "); String resultStr = IterUtil.join(result, " ");
Assert.assertEquals("这 两个 方法 的 区别 在于 返回值", resultStr); Assert.assertEquals("这 两个 方法 的 区别 在于 返回值", resultStr);
} }
@Test @Test
public void mmsegTest() { public void mmsegTest() {
TokenizerEngine engine = new MmsegEngine(); TokenizerEngine engine = new MmsegEngine();
Result result = engine.parse(text); Result result = engine.parse(text);
checkResult(result); checkResult(result);
} }
@Test @Test
public void smartcnTest() { public void smartcnTest() {
TokenizerEngine engine = new SmartcnEngine(); TokenizerEngine engine = new SmartcnEngine();
Result result = engine.parse(text); Result result = engine.parse(text);
String resultStr = IterUtil.join((Iterator<Word>)result, " "); String resultStr = IterUtil.join(result, " ");
Assert.assertEquals("这 两 个 方法 的 区别 在于 返回 值", resultStr); Assert.assertEquals("这 两 个 方法 的 区别 在于 返回 值", resultStr);
} }
@Test @Test
public void wordTest() { public void wordTest() {
TokenizerEngine engine = new WordEngine(); TokenizerEngine engine = new WordEngine();
Result result = engine.parse(text); Result result = engine.parse(text);
String resultStr = IterUtil.join((Iterator<Word>)result, " "); String resultStr = IterUtil.join(result, " ");
Assert.assertEquals("这两个 方法 的 区别 在于 返回值", resultStr); Assert.assertEquals("这两个 方法 的 区别 在于 返回值", resultStr);
} }
@Test @Test
@Ignore
public void mynlpTest() { public void mynlpTest() {
// 此单元测试需要JDK8默认忽略
TokenizerEngine engine = new MynlpEngine(); TokenizerEngine engine = new MynlpEngine();
Result result = engine.parse(text); Result result = engine.parse(text);
String resultStr = IterUtil.join((Iterator<Word>)result, " "); String resultStr = IterUtil.join(result, " ");
Assert.assertEquals("这 两个 方法 的 区别 在于 返回 值", resultStr); Assert.assertEquals("这 两个 方法 的 区别 在于 返回 值", resultStr);
} }
private void checkResult(Result result) { private void checkResult(Result result) {
String resultStr = IterUtil.join((Iterator<Word>)result, " "); String resultStr = IterUtil.join(result, " ");
Assert.assertEquals("这 两个 方法 的 区别 在于 返回 值", resultStr); Assert.assertEquals("这 两个 方法 的 区别 在于 返回 值", resultStr);
} }
} }