Repository: deepjavalibrary/djl
Pull request #3589 (2025-01-22 06:16:55)

Adds zero-shot-classification to model zoo
## Description ##

Brief description of what this PR is about

- If this change is a backward incompatible change, why must this change be made?
- Interesting edge cases to note here



Patch:
diff --git a/api/src/main/java/ai/djl/modality/cv/transform/ResizeShort.java b/api/src/main/java/ai/djl/modality/cv/transform/ResizeShort.java
new file mode 100644
index 000000000..9f443533d
--- /dev/null
+++ b/api/src/main/java/ai/djl/modality/cv/transform/ResizeShort.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+package ai.djl.modality.cv.transform;
+
+import ai.djl.modality.cv.Image;
+import ai.djl.modality.cv.util.NDImageUtils;
+import ai.djl.ndarray.NDArray;
+import ai.djl.ndarray.types.Shape;
+import ai.djl.translate.Transform;
+
+/** A {@link Transform} that resizes the image and keep aspect ratio. */
+public class ResizeShort implements Transform {
+
+    private int shortEdge;
+    private int longEdge;
+    private Image.Interpolation interpolation;
+
+    /**
+     * Creates a {@code ResizeShort} {@link Transform} that resizes to the given size.
+     *
+     * @param shortEdge the length of the short edge
+     */
+    public ResizeShort(int shortEdge) {
+        this(shortEdge, -1, Image.Interpolation.BILINEAR);
+    }
+
+    /**
+     * Creates a {@code ResizeShort} {@link Transform} that resizes to the given size and given
+     * interpolation.
+     *
+     * @param shortEdge the length of the short edge
+     * @param longEdge the length of the long edge
+     * @param interpolation the desired interpolation
+     */
+    public ResizeShort(int shortEdge, int longEdge, Image.Interpolation interpolation) {
+        this.shortEdge = shortEdge;
+        this.longEdge = longEdge;
+        this.interpolation = interpolation;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public NDArray transform(NDArray array) {
+        Shape shape = array.getShape();
+        int width = (int) shape.get(1);
+        int height = (int) shape.get(0);
+        int min = Math.min(width, height);
+        int max = Math.max(width, height);
+        int newShort;
+        int newLong;
+
+        if (shortEdge < 0) {
+            newShort = min * longEdge / max;
+            newLong = longEdge;
+        } else {
+            newShort = shortEdge;
+            newLong = max * shortEdge / min;
+            if (longEdge > 0 && newLong > longEdge) {
+                newShort = min * longEdge / max;
+                newLong = longEdge;
+            }
+        }
+        int rescaledWidth;
+        int rescaledHeight;
+        if (width > height) {
+            rescaledWidth = newLong;
+            rescaledHeight = newShort;
+        } else {
+            rescaledWidth = newShort;
+            rescaledHeight = newLong;
+        }
+
+        return NDImageUtils.resize(array, rescaledWidth, rescaledHeight, interpolation);
+    }
+}
diff --git a/api/src/main/java/ai/djl/modality/cv/translator/BaseImageTranslator.java b/api/src/main/java/ai/djl/modality/cv/translator/BaseImageTranslator.java
index 675c4fb2f..fb508e353 100644
--- a/api/src/main/java/ai/djl/modality/cv/translator/BaseImageTranslator.java
+++ b/api/src/main/java/ai/djl/modality/cv/translator/BaseImageTranslator.java
@@ -18,9 +18,12 @@ import ai.djl.modality.cv.transform.CenterCrop;
 import ai.djl.modality.cv.transform.CenterFit;
 import ai.djl.modality.cv.transform.Normalize;
 import ai.djl.modality.cv.transform.Resize;
+import ai.djl.modality.cv.transform.ResizeShort;
 import ai.djl.modality.cv.transform.ToTensor;
+import ai.djl.modality.cv.util.NDImageUtils;
 import ai.djl.ndarray.NDArray;
 import ai.djl.ndarray.NDList;
+import ai.djl.ndarray.types.Shape;
 import ai.djl.translate.ArgumentsUtil;
 import ai.djl.translate.Batchifier;
 import ai.djl.translate.Pipeline;
@@ -77,9 +80,23 @@ public abstract class BaseImageTranslator<T> implements Translator<Image, T> {
     @Override
     public NDList processInput(TranslatorContext ctx, Image input) {
         NDArray array = input.toNDArray(ctx.getNDManager(), flag);
+        NDList list = pipeline.transform(new NDList(array));
+        Shape shape = list.get(0).getShape();
+        int processedWidth;
+        int processedHeight;
+        long[] dim = shape.getShape();
+        if (NDImageUtils.isCHW(shape)) {
+            processedWidth = (int) dim[dim.length - 1];
+            processedHeight = (int) dim[dim.length - 2];
+        } else {
+            processedWidth = (int) dim[dim.length - 2];
+            processedHeight = (int) dim[dim.length - 3];
+        }
         ctx.setAttachment("width", input.getWidth());
         ctx.setAttachment("height", input.getHeight());
-        return pipeline.transform(new NDList(array));
+        ctx.setAttachment("processedWidth", processedWidth);
+        ctx.setAttachment("processedHeight", processedHeight);
+        return list;
     }
 
     /**
@@ -116,13 +133,6 @@ public abstract class BaseImageTranslator<T> implements Translator<Image, T> {
          */
         public T setPipeline(Pipeline pipeline) {
             this.pipeline = pipeline;
-            for (Transform transform : pipeline.getTransforms()) {
-                if (transform instanceof Resize) {
-                    Resize resize = (Resize) transform;
-                    width = resize.getWidth();
-                    height = resize.getHeight();
-                }
-            }
             return self();
         }
 
@@ -149,12 +159,6 @@ public abstract class BaseImageTranslator<T> implements Translator<Image, T> {
             if (pipeline == null) {
                 pipeline = new Pipeline();
             }
-            if (transform instanceof Resize) {
-                Resize resize = (Resize) transform;
-                width = resize.getWidth();
-                height = resize.getHeight();
-            }
-
             pipeline.add(transform);
             return self();
         }
@@ -192,21 +196,46 @@ public abstract class BaseImageTranslator<T> implements Translator<Image, T> {
                 addTransform(new Resize(width, height));
             } else if (!"false".equals(resize)) {
                 String[] tokens = resize.split("\\s*,\\s*");
+                int w = (int) Double.parseDouble(tokens[0]);
+                int h;
+                Image.Interpolation interpolation;
                 if (tokens.length > 1) {
-                    setImageSize(
-                            (int) Double.parseDouble(tokens[0]),
-                            (int) Double.parseDouble(tokens[1]));
+                    h = (int) Double.parseDouble(tokens[1]);
                 } else {
-                    int size = (int) Double.parseDouble(tokens[0]);
-                    setImageSize(size, size);
+                    h = w;
                 }
-                addTransform(new Resize(width, height));
+                if (tokens.length > 2) {
+                    interpolation = Image.Interpolation.valueOf(tokens[2]);
+                } else {
+                    interpolation = Image.Interpolation.BILINEAR;
+                }
+                addTransform(new Resize(w, h, interpolation));
+            }
+            String resizeShort = ArgumentsUtil.stringValue(arguments, "resizeShort", "false");
+            if ("true".equals(resizeShort)) {
+                int shortEdge = Math.max(width, height);
+                addTransform(new ResizeShort(shortEdge));
+            } else if (!"false".equals(resizeShort)) {
+                String[] tokens = resizeShort.split("\\s*,\\s*");
+                int shortEdge = (int) Double.parseDouble(tokens[0]);
+                int longEdge;
+                Image.Interpolation interpolation;
+                if (tokens.length > 1) {
+                    longEdge = (int) Double.parseDouble(tokens[1]);
+                } else {
+                    longEdge = -1;
+                }
+                if (tokens.length > 2) {
+                    interpolation = Image.Interpolation.valueOf(tokens[2]);
+                } else {
+                    interpolation = Image.Interpolation.BILINEAR;
+                }
+                addTransform(new ResizeShort(shortEdge, longEdge, interpolation));
             }
             if (ArgumentsUtil.booleanValue(arguments, "centerCrop", false)) {
                 addTransform(new CenterCrop(width, height));
             }
-            String centerFit = ArgumentsUtil.stringValue(arguments, "centerFit", "false");
-            if ("true".equals(centerFit)) {
+            if (ArgumentsUtil.booleanValue(arguments, "centerFit")) {
                 addTransform(new CenterFit(width, height));
             }
             if (ArgumentsUtil.booleanValue(arguments, "toTensor", true)) {
@@ -297,6 +326,21 @@ public abstract class BaseImageTranslator<T> implements Translator<Image, T> {
             if (synsetLoader == null) {
                 synsetLoader = new SynsetLoader("synset.txt");
             }
+            boolean hasCrop = false;
+            boolean sizeMismatch = false;
+            for (Transform transform : pipeline.getTransforms()) {
+                if (transform instanceof Resize) {
+                    Resize resize = (Resize) transform;
+                    if (width != resize.getWidth() || height != resize.getHeight()) {
+                        sizeMismatch = true;
+                    }
+                } else if (transform instanceof CenterCrop || transform instanceof CenterFit) {
+                    hasCrop = true;
+                }
+            }
+            if (sizeMismatch && !hasCrop) {
+                throw new IllegalArgumentException("resized image has mismatched target size");
+            }
         }
 
         /** {@inheritDoc} */
diff --git a/api/src/main/java/ai/djl/modality/cv/translator/InstanceSegmentationTranslator.java b/api/src/main/java/ai/djl/modality/cv/translator/InstanceSegmentationTranslator.java
index 02856c13f..be1cc4a6d 100644
--- a/api/src/main/java/ai/djl/modality/cv/translator/InstanceSegmentationTranslator.java
+++ b/api/src/main/java/ai/djl/modality/cv/translator/InstanceSegmentationTranslator.java
@@ -12,15 +12,14 @@
  */
 package ai.djl.modality.cv.translator;
 
+import ai.djl.modality.cv.Image;
 import ai.djl.modality.cv.output.BoundingBox;
 import ai.djl.modality.cv.output.DetectedObjects;
 import ai.djl.modality.cv.output.Mask;
-import ai.djl.modality.cv.util.NDImageUtils;
+import ai.djl.modality.cv.transform.ResizeShort;
 import ai.djl.ndarray.NDArray;
 import ai.djl.ndarray.NDList;
-import ai.djl.ndarray.types.Shape;
 import ai.djl.translate.ArgumentsUtil;
-import ai.djl.translate.Transform;
 import ai.djl.translate.TranslatorContext;
 
 import java.io.IOException;
@@ -36,11 +35,6 @@ public class InstanceSegmentationTranslator extends BaseImageTranslator<Detected
 
     private SynsetLoader synsetLoader;
     private float threshold;
-    private int shortEdge;
-    private int maxEdge;
-
-    private int rescaledWidth;
-    private int rescaledHeight;
 
     private List<String> classes;
 
@@ -53,10 +47,6 @@ public class InstanceSegmentationTranslator extends BaseImageTranslator<Detected
         super(builder);
         this.synsetLoader = builder.synsetLoader;
         this.threshold = builder.threshold;
-        this.shortEdge = builder.shortEdge;
-        this.maxEdge = builder.maxEdge;
-
-        pipeline.insert(0, null, new ResizeShort());
     }
 
     /** {@inheritDoc} */
@@ -70,6 +60,9 @@ public class InstanceSegmentationTranslator extends BaseImageTranslator<Detected
     /** {@inheritDoc} */
     @Override
     public DetectedObjects processOutput(TranslatorContext ctx, NDList list) {
+        int rescaledWidth = (Integer) ctx.getAttachment("processedWidth");
+        int rescaledHeight = (Integer) ctx.getAttachment("processedHeight");
+
         float[] ids = list.get(0).toFloatArray();
         float[] scores = list.get(1).toFloatArray();
         NDArray boundingBoxes = list.get(2);
@@ -129,28 +122,6 @@ public class InstanceSegmentationTranslator extends BaseImageTranslator<Detected
         return builder;
     }
 
-    /** Resizes the image based on the shorter edge or maximum edge length. */
-    private class ResizeShort implements Transform {
-
-        /** {@inheritDoc} */
-        @Override
-        public NDArray transform(NDArray array) {
-            Shape shape = array.getShape();
-            int width = (int) shape.get(1);
-            int height = (int) shape.get(0);
-            int min = Math.min(width, height);
-            int max = Math.max(width, height);
-            float scale = shortEdge / (float) min;
-            if (Math.round(scale * max) > maxEdge) {
-                scale = maxEdge / (float) max;
-            }
-            rescaledHeight = Math.round(height * scale);
-            rescaledWidth = Math.round(width * scale);
-
-            return NDImageUtils.resize(array, rescaledWidth, rescaledHeight);
-        }
-    }
-
     /** The builder for Instance Segmentation translator. */
     public static class Builder extends ClassificationBuilder<Builder> {
 
@@ -217,6 +188,9 @@ public class InstanceSegmentationTranslator extends BaseImageTranslator<Detected
          */
         public InstanceSegmentationTranslator build() {
             validate();
+            ResizeShort resize = new ResizeShort(shortEdge, maxEdge, Image.Interpolation.BILINEAR);
+            pipeline.insert(0, null, resize);
+
             return new InstanceSegmentationTranslator(this);
         }
     }
diff --git a/api/src/main/java/ai/djl/modality/cv/translator/SemanticSegmentationTranslator.java b/api/src/main/java/ai/djl/modality/cv/translator/SemanticSegmentationTranslator.java
index 9ca5942a7..2b1c3753e 100644
--- a/api/src/main/java/ai/djl/modality/cv/translator/SemanticSegmentationTranslator.java
+++ b/api/src/main/java/ai/djl/modality/cv/translator/SemanticSegmentationTranslator.java
@@ -14,12 +14,10 @@ package ai.djl.modality.cv.translator;
 
 import ai.djl.modality.cv.Image;
 import ai.djl.modality.cv.output.CategoryMask;
-import ai.djl.modality.cv.util.NDImageUtils;
-import ai.djl.ndarray.NDArray;
+import ai.djl.modality.cv.transform.ResizeShort;
 import ai.djl.ndarray.NDList;
 import ai.djl.ndarray.types.Shape;
 import ai.djl.translate.ArgumentsUtil;
-import ai.djl.translate.Transform;
 import ai.djl.translate.Translator;
 import ai.djl.translate.TranslatorContext;
 
@@ -34,8 +32,6 @@ import java.util.Map;
 public class SemanticSegmentationTranslator extends BaseImageTranslator<CategoryMask> {
 
     private SynsetLoader synsetLoader;
-    private final int shortEdge;
-    private final int maxEdge;
 
     private List<String> classes;
 
@@ -47,10 +43,6 @@ public class SemanticSegmentationTranslator extends BaseImageTranslator<Category
     public SemanticSegmentationTranslator(Builder builder) {
         super(builder);
         this.synsetLoader = builder.synsetLoader;
-        this.shortEdge = builder.shortEdge;
-        this.maxEdge = builder.maxEdge;
-
-        pipeline.insert(0, null, new ResizeShort());
     }
 
     /** {@inheritDoc} */
@@ -61,12 +53,6 @@ public class SemanticSegmentationTranslator extends BaseImageTranslator<Category
         }
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public NDList processInput(TranslatorContext ctx, Image image) {
-        return super.processInput(ctx, image);
-    }
-
     /** {@inheritDoc} */
     @Override
     public CategoryMask processOutput(TranslatorContext ctx, NDList list) {
@@ -124,27 +110,6 @@ public class SemanticSegmentationTranslator extends BaseImageTranslator<Category
         return builder;
     }
 
-    /** Resizes the image based on the shorter edge or maximum edge length. */
-    private class ResizeShort implements Transform {
-        /** {@inheritDoc} */
-        @Override
-        public NDArray transform(NDArray array) {
-            Shape shape = array.getShape();
-            int width = (int) shape.get(1);
-            int height = (int) shape.get(0);
-            int min = Math.min(width, height);
-            int max = Math.max(width, height);
-            float scale = shortEdge / (float) min;
-            if (Math.round(scale * max) > maxEdge) {
-                scale = maxEdge / (float) max;
-            }
-            int rescaledHeight = Math.round(height * scale);
-            int rescaledWidth = Math.round(width * scale);
-
-            return NDImageUtils.resize(array, rescaledWidth, rescaledHeight);
-        }
-    }
-
     /** The builder for Semantic Segmentation translator. */
     public static class Builder extends ClassificationBuilder<Builder> {
         int shortEdge = 600;
@@ -173,6 +138,9 @@ public class SemanticSegmentationTranslator extends BaseImageTranslator<Category
          */
         public SemanticSegmentationTranslator build() {
             validate();
+            ResizeShort resize = new ResizeShort(shortEdge, maxEdge, Image.Interpolation.BILINEAR);
+            pipeline.insert(0, null, resize);
+
             return new SemanticSegmentationTranslator(this);
         }
     }
diff --git a/api/src/main/java/ai/djl/ndarray/NDArrayAdapter.java b/api/src/main/java/ai/djl/ndarray/NDArrayAdapter.java
index cceb5efd4..b27cd0ac1 100644
--- a/api/src/main/java/ai/djl/ndarray/NDArrayAdapter.java
+++ b/api/src/main/java/ai/djl/ndarray/NDArrayAdapter.java
@@ -40,7 +40,7 @@ public abstract class NDArrayAdapter implements NDArray {
 
     protected NDManager manager;
     protected NDManager alternativeManager;
-    private NDArray alternativeArray;
+    protected NDArray alternativeArray;
 
     protected Shape shape;
     protected DataType dataType;
diff --git a/api/src/main/native/djl/utils.h b/api/src/main/native/djl/utils.h
index cd526c762..b67fb8fc9 100644
--- a/api/src/main/native/djl/utils.h
+++ b/api/src/main/native/djl/utils.h
@@ -14,6 +14,7 @@
 #ifndef DJL_UTILS_H
 #define DJL_UTILS_H
 
+#include <cstdint>
 #include <jni.h>
 #include <vector>
 #include <string>
diff --git a/engines/ml/xgboost/src/main/java/ai/djl/ml/xgboost/XgbNDArray.java b/engines/ml/xgboost/src/main/java/ai/djl/ml/xgboost/XgbNDArray.java
index 69eb6914b..990fad712 100644
--- a/engines/ml/xgboost/src/main/java/ai/djl/ml/xgboost/XgbNDArray.java
+++ b/engines/ml/xgboost/src/main/java/ai/djl/ml/xgboost/XgbNDArray.java
@@ -88,14 +88,33 @@ public class XgbNDArray extends NDArrayAdapter {
     /** {@inheritDoc} */
     @Override
     public void intern(NDArray replaced) {
+        if (!(replaced instanceof XgbNDArray)) {
+            throw new IllegalArgumentException(
+                    "The replaced NDArray must be an instance of XgbNDArray.");
+        }
+        XgbNDArray array = (XgbNDArray) replaced;
+        if (isReleased()) {
+            throw new IllegalArgumentException("This array is already closed");
+        }
+        if (replaced.isReleased()) {
+            throw new IllegalArgumentException("This target array is already closed");
+        }
+
         if (handle != null && handle.get() != 0L) {
             long pointer = handle.getAndSet(0L);
             JniUtils.deleteDMatrix(pointer);
         }
-        XgbNDArray array = (XgbNDArray) replaced;
+        if (alternativeArray != null) {
+            alternativeArray.close();
+        }
+
         data = array.data;
         handle = array.handle;
         format = array.format;
+        alternativeArray = array.alternativeArray;
+        array.handle = null;
+        array.alternativeArray = null;
+        array.close();
     }
 
     /** {@inheritDoc} */
diff --git a/engines/onnxruntime/onnxruntime-engine/src/main/java/ai/djl/onnxruntime/zoo/OrtHfModelZoo.java b/engines/onnxruntime/onnxruntime-engine/src/main/java/ai/djl/onnxruntime/zoo/OrtHfModelZoo.java
index 315a5ef4c..22d684f11 100644
--- a/engines/onnxruntime/onnxruntime-engine/src/main/java/ai/djl/onnxruntime/zoo/OrtHfModelZoo.java
+++ b/engines/onnxruntime/onnxruntime-engine/src/main/java/ai/djl/onnxruntime/zoo/OrtHfModelZoo.java
@@ -72,6 +72,7 @@ public class OrtHfModelZoo extends ModelZoo {
                     addModels(Application.NLP.TEXT_CLASSIFICATION, version);
                     addModels(Application.NLP.TEXT_EMBEDDING, version);
                     addModels(Application.NLP.TOKEN_CLASSIFICATION, version);
+                    addModels(Application.NLP.ZERO_SHOT_CLASSIFICATION, version);
                     initialized = true;
                 }
             }
diff --git a/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtNDArrayEx.java b/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtNDArrayEx.java
index 9fe5809fb..64ab46acd 100644
--- a/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtNDArrayEx.java
+++ b/engines/pytorch/pytorch-engine/src/main/java/ai/djl/pytorch/engine/PtNDArrayEx.java
@@ -602,6 +602,7 @@ public class PtNDArrayEx implements NDArrayEx {
             if (dim == 3) {
                 result = result.squeeze(0);
             }
+            result = result.toType(array.getDataType(), false);
             array.attach(subManager.getParentManager());
             result.attach(subManager.getParentManager());
             return (PtNDArray) result;
diff --git a/engines/pytorch/pytorch-native/src/main/native/ai_djl_pytorch_jni_PyTorchLibrary_nn_functional.cc b/engines/pytorch/pytorch-native/src/main/native/ai_djl_pytorch_jni_PyTorchLibrary_nn_functional.cc
index 033cc5576..5453692a6 100644
--- a/engines/pytorch/pytorch-native/src/main/native/ai_djl_pytorch_jni_PyTorchLibrary_nn_functional.cc
+++ b/engines/pytorch/pytorch-native/src/main/native/ai_djl_pytorch_jni_PyTorchLibrary_nn_functional.cc
@@ -80,7 +80,7 @@ JNIEXPORT jlong JNICALL Java_ai_djl_pytorch_jni_PyTorchLibrary_torchNNInterpolat
       torch::nn::functional::InterpolateFuncOptions().size(size_vec).mode(utils::GetInterpolationMode(jmode));
   // kNearest, kArea interpolate can't set align_corners
   if (jmode != 0 && jmode != 5) {
-    options = options.align_corners(jalign_corners);
+    options = options.align_corners(jalign_corners).antialias(true);
   }
   const auto* result_ptr = new torch::Tensor(torch::nn::functional::interpolate(*tensor_ptr, options));
 #endif
diff --git a/extensions/tokenizers/src/main/java/ai/djl/huggingface/zoo/HfModelZoo.java b/extensions/tokenizers/src/main/java/ai/djl/huggingface/zoo/HfModelZoo.java
index 537bae350..b8a0b76f8 100644
--- a/extensions/tokenizers/src/main/java/ai/djl/huggingface/zoo/HfModelZoo.java
+++ b/extensions/tokenizers/src/main/java/ai/djl/huggingface/zoo/HfModelZoo.java
@@ -73,6 +73,7 @@ public class HfModelZoo extends ModelZoo {
                     addModels(NLP.TEXT_CLASSIFICATION, version);
                     addModels(NLP.TEXT_EMBEDDING, version);
                     addModels(NLP.TOKEN_CLASSIFICATION, version);
+                    addModels(NLP.ZERO_SHOT_CLASSIFICATION, version);
                     initialized = true;
                 }
             }


Test patch:
diff --git a/api/src/test/java/ai/djl/modality/cv/transform/ResizeShortTest.java b/api/src/test/java/ai/djl/modality/cv/transform/ResizeShortTest.java
new file mode 100644
index 000000000..0205179ce
--- /dev/null
+++ b/api/src/test/java/ai/djl/modality/cv/transform/ResizeShortTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+package ai.djl.modality.cv.transform;
+
+import ai.djl.modality.cv.Image;
+import ai.djl.ndarray.NDArray;
+import ai.djl.ndarray.NDManager;
+import ai.djl.ndarray.types.Shape;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class ResizeShortTest {
+
+    @Test
+    public void test() {
+        try (NDManager manager = NDManager.newBaseManager()) {
+            NDArray array = manager.zeros(new Shape(100, 50, 3));
+
+            ResizeShort resize = new ResizeShort(20);
+            NDArray output = resize.transform(array);
+            long[] shape = output.getShape().getShape();
+            Assert.assertEquals(shape[0], 40);
+            Assert.assertEquals(shape[1], 20);
+
+            resize = new ResizeShort(-1, 20, Image.Interpolation.BILINEAR);
+            output = resize.transform(array);
+            shape = output.getShape().getShape();
+            Assert.assertEquals(shape[0], 20);
+
+            resize = new ResizeShort(30, 20, Image.Interpolation.BILINEAR);
+            output = resize.transform(array);
+            shape = output.getShape().getShape();
+            Assert.assertEquals(shape[0], 20);
+        }
+    }
+}
diff --git a/api/src/test/java/ai/djl/modality/cv/transform/package-info.java b/api/src/test/java/ai/djl/modality/cv/transform/package-info.java
new file mode 100644
index 000000000..c631fafea
--- /dev/null
+++ b/api/src/test/java/ai/djl/modality/cv/transform/package-info.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
+ * with the License. A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0/
+ *
+ * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ */
+/** Contains tests for {@link ai.djl.modality.cv.transform}. */
+package ai.djl.modality.cv.transform;
diff --git a/api/src/test/java/ai/djl/modality/cv/translator/ImageClassificationTranslatorFactoryTest.java b/api/src/test/java/ai/djl/modality/cv/translator/ImageClassificationTranslatorFactoryTest.java
index 3bb221f01..6a2733fed 100644
--- a/api/src/test/java/ai/djl/modality/cv/translator/ImageClassificationTranslatorFactoryTest.java
+++ b/api/src/test/java/ai/djl/modality/cv/translator/ImageClassificationTranslatorFactoryTest.java
@@ -48,18 +48,27 @@ public class ImageClassificationTranslatorFactoryTest {
     public void testNewInstance() {
         Map<String, String> arguments = new HashMap<>();
         try (Model model = Model.newInstance("test")) {
+            arguments.put("resizeShort", "true");
+            arguments.put("resize", "true");
             Translator<Image, Classifications> translator1 =
                     factory.newInstance(Image.class, Classifications.class, model, arguments);
             Assert.assertTrue(translator1 instanceof ImageClassificationTranslator);
 
+            arguments.put("resize", "224");
+            arguments.put("resizeShort", "224");
             Translator<Path, Classifications> translator2 =
                     factory.newInstance(Path.class, Classifications.class, model, arguments);
             Assert.assertTrue(translator2 instanceof BasicTranslator);
 
+            arguments.put("resize", "224,224");
+            arguments.put("resizeShort", "224,500,BICUBIC");
             Translator<URL, Classifications> translator3 =
                     factory.newInstance(URL.class, Classifications.class, model, arguments);
             Assert.assertTrue(translator3 instanceof BasicTranslator);
 
+            arguments.put("resize", "2,224,BICUBIC");
+            arguments.put("resizeShort", "224,500,BILINEAR");
+            arguments.put("centerCrop", "true");
             Translator<InputStream, Classifications> translator4 =
                     factory.newInstance(InputStream.class, Classifications.class, model, arguments);
             Assert.assertTrue(translator4 instanceof BasicTranslator);
@@ -75,6 +84,13 @@ public class ImageClassificationTranslatorFactoryTest {
             Assert.assertThrows(
                     IllegalArgumentException.class,
                     () -> factory.newInstance(Image.class, Output.class, model, arguments));
+
+            arguments.put("centerCrop", "false");
+            Assert.assertThrows(
+                    IllegalArgumentException.class,
+                    () ->
+                            factory.newInstance(
+                                    Image.class, Classifications.class, model, arguments));
         }
     }
 }