From 6b2321a7a90b81f246fa64644c1c0ef64b1d9924 Mon Sep 17 00:00:00 2001 From: Umar Hussain Date: Mon, 2 Jun 2025 14:41:40 +0500 Subject: [PATCH] feat(imageio-openjpeg): enable raster read on `OpenJp2ImageReader` The class now overrides the raster functions from `ImageReader` class. The `canReadRaster` returns true enabling the caller to call `readRaster` and `readTileRaster`. The readRaster and readTileRaster call the normal read functions and then return the raster with `getData` call on the buffered image. Test cases are added to compare the rasters on expected and actual images with the option of tolerance in the diff of samples if the quality of images is low. --- .../openjpeg/imageio/OpenJp2ImageReader.java | 16 ++++++ .../imageio/OpenJp2ImageReaderTest.java | 52 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/imageio-openjpeg/src/main/java/de/digitalcollections/openjpeg/imageio/OpenJp2ImageReader.java b/imageio-openjpeg/src/main/java/de/digitalcollections/openjpeg/imageio/OpenJp2ImageReader.java index b4d19ea..8d9f2c9 100644 --- a/imageio-openjpeg/src/main/java/de/digitalcollections/openjpeg/imageio/OpenJp2ImageReader.java +++ b/imageio-openjpeg/src/main/java/de/digitalcollections/openjpeg/imageio/OpenJp2ImageReader.java @@ -5,6 +5,7 @@ import de.digitalcollections.openjpeg.lib.enums.COLOR_SPACE; import java.awt.Rectangle; import java.awt.image.BufferedImage; +import java.awt.image.Raster; import java.io.IOException; import java.util.Iterator; import java.util.stream.Stream; @@ -228,6 +229,21 @@ public BufferedImage readTile(int imageIndex, int tileX, int tileY) throws IOExc return this.read(imageIndex, param); } + @Override + public boolean canReadRaster() { + return true; + } + + @Override + public Raster readRaster(int imageIndex, ImageReadParam param) throws IOException { + return this.read(imageIndex, param).getData(); + } + + @Override + public Raster readTileRaster(int imageIndex, int tileX, int tileY) throws IOException { + return this.readTile(imageIndex,tileX,tileY).getData(); + } + @Override public IIOMetadata getStreamMetadata() { return null; diff --git a/imageio-openjpeg/src/test/java/de/digitalcollections/openjpeg/imageio/OpenJp2ImageReaderTest.java b/imageio-openjpeg/src/test/java/de/digitalcollections/openjpeg/imageio/OpenJp2ImageReaderTest.java index 6fafbb2..bc8fcdc 100644 --- a/imageio-openjpeg/src/test/java/de/digitalcollections/openjpeg/imageio/OpenJp2ImageReaderTest.java +++ b/imageio-openjpeg/src/test/java/de/digitalcollections/openjpeg/imageio/OpenJp2ImageReaderTest.java @@ -5,6 +5,7 @@ import java.awt.Rectangle; import java.awt.image.BufferedImage; +import java.awt.image.Raster; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -152,29 +153,79 @@ private void assertImageEquals(String expectedImageName, String actualImageName) assertImageEquals(expectedImg, actualImage); } + public static void compareImagesByRaster(BufferedImage expectedImage, BufferedImage actualImage, + Raster actualRaster, int tolerance) { + int width = expectedImage.getWidth(); + assertEquals(width, actualImage.getWidth()); + int height = expectedImage.getHeight(); + assertEquals(height, actualImage.getHeight()); + + Raster expectedRaster = expectedImage.getData(); + int bands = expectedRaster.getNumBands(); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int totalDiff = 0; + for (int b = 0; b < bands; b++) { + int expectedSample = expectedRaster.getSample(x, y, b); + int actualSample = actualRaster.getSample(x, y, b); + totalDiff += Math.abs(expectedSample - actualSample); + } + if (totalDiff > tolerance) { + assertEquals(totalDiff, tolerance, "Tolerance of Raster " + x + "," + y); + } + } + } + } + + private void assertRasterEquals(String expectedImageName, String actualImageName) + throws IOException { + assertRasterEquals(expectedImageName, actualImageName, 5); + } + + private void assertRasterEquals(String expectedImageName, String actualImageName, int tolerance) + throws IOException { + OpenJp2ImageReader reader = getReader(actualImageName); + BufferedImage actualImage = reader.read(0, null); + reader = getReader(actualImageName); + Raster actualRaster = reader.readRaster(0, null); + InputStream input = ClassLoader.getSystemResourceAsStream(expectedImageName); + assertThat(input).isNotNull(); + BufferedImage expectedImg = ImageIO.read(input); + + compareImagesByRaster(expectedImg, actualImage, actualRaster, tolerance); + } + @Test public void testReadRGBA() throws Exception { assertImageEquals("rgba.png", "rgba.jp2"); + assertRasterEquals("rgba.png", "rgba.jp2"); } @Test public void testReadCMYK() throws Exception { assertImageEquals("cmyk.png", "cmyk.jp2"); + // need higher tolerance due to lower image quality which will appear in raster sample + assertRasterEquals("cmyk.png", "cmyk.jp2", 1500); } @Test public void testReadCMYK_withAlpha() throws Exception { assertImageEquals("cmykWithAlpha.png", "cmykWithAlpha.jp2"); + // need higher tolerance due to lower image quality which will appear in raster sample + assertRasterEquals("cmykWithAlpha.png", "cmykWithAlpha.jp2",1500); } @Test public void testReadGrayWithAlpha() throws Exception { assertImageEquals("grayWithAlpha.png", "grayWithAlpha.jp2"); + assertRasterEquals("grayWithAlpha.png", "grayWithAlpha.jp2"); } @Test public void testReadGray16bitWithoutAlpha() throws Exception { assertImageEquals("gray16bitWithoutAlpha.png", "gray16bitWithoutAlpha.jp2"); + assertRasterEquals("gray16bitWithoutAlpha.png", "gray16bitWithoutAlpha.jp2"); } @Test @@ -190,5 +241,6 @@ public void testReadYUV() throws Exception { @Test public void testReadWeirdGrayscale() throws IOException { assertImageEquals("gray2_control.png", "gray2.jp2"); + assertRasterEquals("gray2_control.png", "gray2.jp2"); } }