NIFI-10392 - This closes #6330. ResizeImage - add option to maintain aspect ratio

Signed-off-by: Joe Witt <joewitt@apache.org>
This commit is contained in:
Pierre Villard 2022-08-24 12:39:43 +02:00 committed by Joe Witt
parent 777238eb32
commit 71ba581fd8
No known key found for this signature in database
GPG Key ID: 9093BF854F811A1A
2 changed files with 82 additions and 10 deletions

View File

@ -17,6 +17,7 @@
package org.apache.nifi.processors.image;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Transparency;
@ -88,6 +89,15 @@ public class ResizeImage extends AbstractProcessor {
.allowableValues(RESIZE_DEFAULT, RESIZE_FAST, RESIZE_SMOOTH, RESIZE_REPLICATE, RESIZE_AREA_AVERAGING)
.defaultValue(RESIZE_DEFAULT.getValue())
.build();
static final PropertyDescriptor KEEP_RATIO = new PropertyDescriptor.Builder()
.displayName("Maintain aspect ratio")
.name("keep-ratio")
.description("Specifies if the ratio of the input image should be maintained")
.expressionLanguageSupported(ExpressionLanguageScope.FLOWFILE_ATTRIBUTES)
.allowableValues("true", "false")
.defaultValue("false")
.required(true)
.build();
static final Relationship REL_SUCCESS = new Relationship.Builder()
.name("success")
@ -104,6 +114,7 @@ public class ResizeImage extends AbstractProcessor {
properties.add(IMAGE_WIDTH);
properties.add(IMAGE_HEIGHT);
properties.add(SCALING_ALGORITHM);
properties.add(KEEP_RATIO);
return properties;
}
@ -122,16 +133,6 @@ public class ResizeImage extends AbstractProcessor {
return;
}
final int width, height;
try {
width = context.getProperty(IMAGE_WIDTH).evaluateAttributeExpressions(flowFile).asInteger();
height = context.getProperty(IMAGE_HEIGHT).evaluateAttributeExpressions(flowFile).asInteger();
} catch (final NumberFormatException nfe) {
getLogger().error("Failed to resize {} due to {}", new Object[] { flowFile, nfe });
session.transfer(flowFile, REL_FAILURE);
return;
}
final String algorithm = context.getProperty(SCALING_ALGORITHM).getValue();
final int hints;
if (algorithm.equalsIgnoreCase(RESIZE_DEFAULT.getValue())) {
@ -176,6 +177,25 @@ public class ResizeImage extends AbstractProcessor {
return;
}
final boolean keepRatio = context.getProperty(KEEP_RATIO).evaluateAttributeExpressions(flowFile).asBoolean();
int width, height;
try {
width = context.getProperty(IMAGE_WIDTH).evaluateAttributeExpressions(flowFile).asInteger();
height = context.getProperty(IMAGE_HEIGHT).evaluateAttributeExpressions(flowFile).asInteger();
if(keepRatio) {
Dimension finalDimension = getScaledDimension(image.getWidth(), image.getHeight(), width, height);
width = finalDimension.width;
height = finalDimension.height;
}
} catch (final NumberFormatException nfe) {
getLogger().error("Failed to resize {} due to {}", new Object[] { flowFile, nfe });
session.transfer(flowFile, REL_FAILURE);
return;
}
try (final OutputStream out = session.write(flowFile)) {
final Image scaledImage = image.getScaledInstance(width, height, hints);
final BufferedImage scaledBufferedImg;
@ -207,4 +227,12 @@ public class ResizeImage extends AbstractProcessor {
session.getProvenanceReporter().modifyContent(flowFile, stopWatch.getElapsed(TimeUnit.MILLISECONDS));
session.transfer(flowFile, REL_SUCCESS);
}
public Dimension getScaledDimension(int originalWidth, int originalHeight, int boundWidth, int boundHeight) {
double widthRatio = ((double) boundWidth) / originalWidth;
double heightRatio = ((double) boundHeight) / originalHeight;
double ratio = Math.min(widthRatio, heightRatio);
return new Dimension((int) (originalWidth * ratio), (int) (originalHeight * ratio));
}
}

View File

@ -157,4 +157,48 @@ public class TestResizeImage {
final File out = new File("target/mspaint-8x10resized.png");
ImageIO.write(img, "PNG", out);
}
@Test
public void testResizeBiggerPNGWithRatio() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new ResizeImage());
runner.setProperty(ResizeImage.IMAGE_HEIGHT, "64");
runner.setProperty(ResizeImage.IMAGE_WIDTH, "64");
runner.setProperty(ResizeImage.SCALING_ALGORITHM, ResizeImage.RESIZE_SMOOTH);
runner.setProperty(ResizeImage.KEEP_RATIO, "true");
runner.enqueue(Paths.get("src/test/resources/mspaint-8x10.png"));
runner.run();
runner.assertAllFlowFilesTransferred(ResizeImage.REL_SUCCESS, 1);
final MockFlowFile mff = runner.getFlowFilesForRelationship(ResizeImage.REL_SUCCESS).get(0);
final byte[] data = mff.toByteArray();
final BufferedImage img = ImageIO.read(new ByteArrayInputStream(data));
assertEquals(42, img.getWidth());
assertEquals(64, img.getHeight());
final File out = new File("target/mspaint-8x10resized.png");
ImageIO.write(img, "PNG", out);
}
@Test
public void testResizeSmallerPNGWithRatio() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new ResizeImage());
runner.setProperty(ResizeImage.IMAGE_HEIGHT, "5");
runner.setProperty(ResizeImage.IMAGE_WIDTH, "5");
runner.setProperty(ResizeImage.SCALING_ALGORITHM, ResizeImage.RESIZE_SMOOTH);
runner.setProperty(ResizeImage.KEEP_RATIO, "true");
runner.enqueue(Paths.get("src/test/resources/mspaint-8x10.png"));
runner.run();
runner.assertAllFlowFilesTransferred(ResizeImage.REL_SUCCESS, 1);
final MockFlowFile mff = runner.getFlowFilesForRelationship(ResizeImage.REL_SUCCESS).get(0);
final byte[] data = mff.toByteArray();
final BufferedImage img = ImageIO.read(new ByteArrayInputStream(data));
assertEquals(3, img.getWidth());
assertEquals(5, img.getHeight());
final File out = new File("target/mspaint-8x10resized.png");
ImageIO.write(img, "PNG", out);
}
}