Skip to content

Commit

Permalink
Merge pull request #131 from dbmdz/jpg-grayscale-numcomps
Browse files Browse the repository at this point in the history
Fix getImageTypes() implementations on readers (fixes #129)
  • Loading branch information
jbaiter authored May 6, 2021
2 parents 97ac087 + bc16b22 commit ec3b7db
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.digitalcollections.openjpeg;

import de.digitalcollections.openjpeg.lib.enums.COLOR_SPACE;
import java.awt.Dimension;
import java.awt.Point;
import java.util.Arrays;
Expand All @@ -19,6 +20,8 @@ public class Info {
private int numTilesY;
private int width;
private int height;
private int bitsPerPixel;
private COLOR_SPACE colorSpace;

void setNumComponents(int numComponents) {
this.numComponents = numComponents;
Expand Down Expand Up @@ -60,6 +63,14 @@ public void setTileOriginY(int tileOriginY) {
this.tileOriginY = tileOriginY;
}

public void setBitsPerPixel(int bitsPerPixel) {
this.bitsPerPixel = bitsPerPixel;
}

public void setColorSpace(COLOR_SPACE colorSpace) {
this.colorSpace = colorSpace;
}

public int getNumComponents() {
return numComponents;
}
Expand Down Expand Up @@ -113,4 +124,12 @@ public List<Dimension> getAvailableTileSizes() {
new Dimension((int) (this.tileWidth / factor), (int) (this.tileHeight / factor)))
.collect(Collectors.toList());
}

public int getBitsPerPixel() {
return bitsPerPixel;
}

public COLOR_SPACE getColorSpace() {
return colorSpace;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ public class OpenJpeg {
@SuppressWarnings("checkstyle:constantname")
private static final opj_msg_callback errorLogFn = (msg, data) -> LOGGER.error(msg.trim());

public static final ColorModel COLOR_MODEL_CMYK =
new ComponentColorModel(
new CMYKColorSpace(), false, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
public static final ColorModel COLOR_MODEL_CMYK_ALPHA =
new ComponentColorModel(
new CMYKColorSpace(), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);

public libopenjp2 lib;
public Runtime runtime;

Expand Down Expand Up @@ -138,6 +145,8 @@ private Info getInfo(Pointer codecPointer, opj_image img) {
info.setTileOriginX(csInfo.tx0.intValue());
info.setTileOriginY(csInfo.ty0.intValue());
info.setNumResolutions(csInfo.m_default_tile_info.tccp_info.get().numresolutions.intValue());
info.setBitsPerPixel(img.comps.get().bpp.intValue());
info.setColorSpace(img.color_space.get());

return info;
} finally {
Expand Down Expand Up @@ -379,9 +388,7 @@ private BufferedImage decode(Pointer stream, Rectangle area, int reduceFactor)
private BufferedImage decodeCMYK(
int width, int height, int numcomps, int colorDepthFactor, opj_image_comp[] comps) {
boolean hasAlpha = numcomps > 4;
ColorModel colorModel =
new ComponentColorModel(
new CMYKColorSpace(), hasAlpha, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
ColorModel colorModel = hasAlpha ? COLOR_MODEL_CMYK_ALPHA : COLOR_MODEL_CMYK;
BufferedImage bufImg =
new BufferedImage(
colorModel, colorModel.createCompatibleWritableRaster(width, height), false, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

import de.digitalcollections.openjpeg.Info;
import de.digitalcollections.openjpeg.OpenJpeg;
import de.digitalcollections.openjpeg.lib.enums.COLOR_SPACE;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
import java.util.stream.Stream;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageTypeSpecifier;
Expand Down Expand Up @@ -104,10 +105,38 @@ public int getHeight(int imageIndex) throws IOException {
@Override
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
checkIndex(imageIndex);
// TODO: Support grayscale?
return Collections.singletonList(
ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR))
.iterator();
int numComps = info.getNumComponents();
ImageTypeSpecifier spec = null;
if (numComps == 3) {
spec = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_3BYTE_BGR);
} else if (numComps == 2) {
spec = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
} else if (numComps == 4) {
if (info.getColorSpace() == COLOR_SPACE.OPJ_CLRSPC_CMYK) {
spec =
new ImageTypeSpecifier(
OpenJpeg.COLOR_MODEL_CMYK_ALPHA,
OpenJpeg.COLOR_MODEL_CMYK_ALPHA.createCompatibleSampleModel(
info.getNativeSize().width, info.getNativeSize().height));
}
spec = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_4BYTE_ABGR);
} else if (numComps == 1) {
spec =
ImageTypeSpecifier.createFromBufferedImageType(
info.getBitsPerPixel() > 1
? BufferedImage.TYPE_BYTE_GRAY
: BufferedImage.TYPE_BYTE_BINARY);
} else if (numComps == 5) {
spec =
new ImageTypeSpecifier(
OpenJpeg.COLOR_MODEL_CMYK,
OpenJpeg.COLOR_MODEL_CMYK.createCompatibleSampleModel(
info.getNativeSize().width, info.getNativeSize().height));
}
if (spec == null) {
return Stream.<ImageTypeSpecifier>empty().iterator();
}
return Stream.of(spec).iterator();
}

private Rectangle adjustRegion(int imageIndex, Rectangle sourceRegion) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public void testReadGrayWithAlpha() throws Exception {

@Test
public void testReadGray16bitWithoutAlpha() throws Exception {
assertImageEquals("gray16bitWithoutAlpha.png", "gray16bitWithoutAlpha.jp2");
assertImageEquals("gray16bitWithoutAlpha.png", "gray16bitWithoutAlpha.jp2");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package de.digitalcollections.turbojpeg.imageio;

import static java.awt.image.BufferedImage.TYPE_3BYTE_BGR;
import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR;
import static java.awt.image.BufferedImage.TYPE_4BYTE_ABGR_PRE;
import static java.awt.image.BufferedImage.TYPE_BYTE_GRAY;

import de.digitalcollections.turbojpeg.Info;
import de.digitalcollections.turbojpeg.TurboJpeg;
import de.digitalcollections.turbojpeg.TurboJpegException;
import de.digitalcollections.turbojpeg.lib.enums.TJCS;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
Expand Down Expand Up @@ -111,9 +110,11 @@ public int getHeight(int imageIndex) throws IOException {

@Override
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
return Stream.of(TYPE_3BYTE_BGR, TYPE_4BYTE_ABGR, TYPE_4BYTE_ABGR_PRE, TYPE_BYTE_GRAY)
.map(ImageTypeSpecifier::createFromBufferedImageType)
.iterator();
return Stream.of(
ImageTypeSpecifier.createFromBufferedImageType(
info.getColorspace() == TJCS.TJCS_GRAY ? TYPE_BYTE_GRAY : TYPE_3BYTE_BGR
)
).iterator();
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.digitalcollections.turbojpeg.imageio;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -52,4 +54,27 @@ public BufferedImageAssert hasDimensions(int width, int height) {
}
return this;
}

public BufferedImageAssert isEqualTo(BufferedImage other) {
int width = other.getWidth();
assertEquals(width, actual.getWidth());
int height = other.getHeight();
assertEquals(height, actual.getHeight());
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int expectedPixel = other.getRGB(x, y);
int actualPixel = actual.getRGB(x, y);
if (expectedPixel >> 24 == 0 && actualPixel >> 24 == 0) {
// transparent
continue;
}
if (expectedPixel != actualPixel) {
failWithMessage(
"Expected pixel with color '%s' at x=%d, y=%d, got '%s'",
expectedPixel, x, y, actualPixel);
}
}
}
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.function.Supplier;
import javax.imageio.ImageIO;
Expand Down Expand Up @@ -267,4 +268,17 @@ void testAdjustMCURegion() {
assertThat(region).isEqualTo(regionExpected);
assertThat(extraCrop).isEqualTo(extraCropExpected);
}

@Test
public void testReadGrayscale() throws IOException {
ImageReader reader = getReader("grayscale.jpg");
assertThat(reader.getRawImageType(0).getNumComponents()).isEqualTo(1);
TurboJpegImageReadParam param = (TurboJpegImageReadParam) reader.getDefaultReadParam();
BufferedImage img = reader.read(0, param);
assertThat(img).hasDimensions(1955, 524);
assertThat(img.getType()).isEqualTo(BufferedImage.TYPE_BYTE_GRAY);
InputStream input = ClassLoader.getSystemResourceAsStream("grayscale_control.png");
BufferedImage controlImg = ImageIO.read(input);
assertThat(img).isEqualTo(controlImg);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package de.digitalcollections.turbojpeg.imageio;

import static org.assertj.core.api.Assertions.assertThat;
import static de.digitalcollections.turbojpeg.imageio.CustomAssertions.assertThat;

import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
Expand Down Expand Up @@ -102,4 +104,28 @@ public void testCanReuseWriter() throws IOException {

assertThat(rgb.toByteArray()).isNotEqualTo(bw.toByteArray());
}

@Test
public void testEncodeGrayscale() throws IOException {
ImageWriter writer =
Streams.stream(ImageIO.getImageWritersByFormatName("jpeg"))
.filter(TurboJpegImageWriter.class::isInstance)
.findFirst()
.get();
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.85f);
BufferedImage in = ImageIO.read(ClassLoader.getSystemResource("grayscale.jpg"));
ByteArrayOutputStream os = new ByteArrayOutputStream();
try (ImageOutputStream ios = ImageIO.createImageOutputStream(os)) {
writer.setOutput(ios);
writer.write(null, new IIOImage(in, null, null), param);
}
os.flush();
Files.write(Paths.get("/tmp/debug.jpg"), os.toByteArray());
assertThat(os.toByteArray()).isNotEmpty();
ByteArrayInputStream bis = new ByteArrayInputStream(os.toByteArray());
BufferedImage jpegImg = ImageIO.read(bis);
assertThat(jpegImg).hasNoPixelsOfColor(255);
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit ec3b7db

Please sign in to comment.