Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix gamma correction for gray tiffs #40

Merged
merged 1 commit into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 21 additions & 32 deletions src/main/java/org/mycore/imagetiler/MCRImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
package org.mycore.imagetiler;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_ProfileGray;
Expand All @@ -28,7 +28,6 @@
import java.awt.image.ColorConvertOp;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;
import java.awt.image.RescaleOp;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
Expand Down Expand Up @@ -125,11 +124,8 @@ public class MCRImage {

private static final short TILE_SIZE_FACTOR = (short) (Math.log(TILE_SIZE) / LOG_2);

private static final double ZOOM_FACTOR = 0.5;

private static final ColorConvertOp COLOR_CONVERT_OP = new ColorConvertOp(null);
private static final ColorConvertOp SRGB_COLOR_CONVERT_OP
= new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_sRGB), null);

public static final float DEFAULT_GAMMA = 1F;

/**
Expand Down Expand Up @@ -187,6 +183,7 @@ public class MCRImage {
}
}


/**
* for internal use only: uses required properties to instantiate.
*
Expand Down Expand Up @@ -397,16 +394,6 @@ private static BufferedImage convertIfNeeded(BufferedImage tile) {
}
final BufferedImage newTile = new BufferedImage(tile.getWidth(), tile.getHeight(),
convertToGray ? BufferedImage.TYPE_BYTE_GRAY : BufferedImage.TYPE_INT_RGB);
if (colorModel.getNumColorComponents() == 1) {
//convert to sRGB first and than to grayscale to handle gamma correction
BufferedImage midStep = new BufferedImage(tile.getWidth(), tile.getHeight(), BufferedImage.TYPE_INT_RGB);
SRGB_COLOR_CONVERT_OP.filter(tile, midStep);
float gamma = getGamma(tile.getColorModel().getColorSpace());
if (gamma != DEFAULT_GAMMA) {
gammaCorrection(gamma, midStep, newTile);
return newTile;
}
}
COLOR_CONVERT_OP.filter(tile, newTile);
return newTile;
}
Expand All @@ -419,13 +406,6 @@ private static float getGamma(ColorSpace colorSpace) {
return DEFAULT_GAMMA;
}

private static void gammaCorrection(float gamma, BufferedImage source, BufferedImage target) {
LOGGER.info("Correcting gamma {}.", gamma);
float gammaCorrectionFactor = (float) Math.pow(1 / gamma, -1);
RescaleOp rop = new RescaleOp(gammaCorrectionFactor, 0, null);
rop.filter(source, target);
}

/**
* @return true, if gray scale image uses color map where every entry uses the same value for each color component
*/
Expand All @@ -450,18 +430,17 @@ private static boolean isFakeGrayScale(ColorModel colorModel) {
* @param image source image
* @return shrinked image
*/
protected static BufferedImage scaleBufferedImage(final BufferedImage image) {
protected static BufferedImage scaleBufferedImage(final BufferedImage image, boolean useScaledInstance) {
LOGGER.debug("Scaling image...");
final int width = image.getWidth();
final int height = image.getHeight();
final int newWidth = (int) Math.ceil(width / 2d);
final int newHeight = (int) Math.ceil(height / 2d);
Image scaledImage = useScaledInstance ? image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH) : null;
final BufferedImage bicubic = new BufferedImage(newWidth, newHeight, getImageType(image));
final Graphics2D bg = bicubic.createGraphics();
bg.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
bg.scale(ZOOM_FACTOR, ZOOM_FACTOR);
bg.drawImage(image, 0, 0, null);
bg.dispose();
Graphics2D graphics = bicubic.createGraphics();
graphics.drawImage(useScaledInstance ? scaledImage : image, 0, 0, newWidth, newHeight, null);
graphics.dispose();
LOGGER.debug("Scaling done: {}x{}", width, height);
return bicubic;
}
Expand Down Expand Up @@ -611,7 +590,7 @@ public MCRTiledPictureProps tile(MCRTileEventHandler eventHandler) throws IOExce
LOGGER.debug("ImageReader: {}", imageReader.getClass());
try (ZipOutputStream zout = getZipOutputStream()) {
setImageSize(imageReader);
doTile(imageReader, zout);
doTile(imageReader, zout, shouldApplyGammaCorrection(imageReader));
writeMetaData(zout);
} finally {
imageReader.dispose();
Expand All @@ -626,6 +605,15 @@ public MCRTiledPictureProps tile(MCRTileEventHandler eventHandler) throws IOExce
return imageProperties;
}

private boolean shouldApplyGammaCorrection(ImageReader imageReader) throws IOException {
ColorModel colorModel = imageReader.getRawImageType(0).getColorModel();
if (colorModel.getNumColorComponents() == 1) {
float gamma = getGamma(colorModel.getColorSpace());
return (gamma != DEFAULT_GAMMA);
}
return false;
}

private void setOrientation() {
long start = System.nanoTime();
short orientation = 1;
Expand Down Expand Up @@ -654,7 +642,8 @@ protected MCROrientation getOrientation() {
return orientation;
}

protected void doTile(final ImageReader imageReader, final ZipOutputStream zout) throws IOException {
protected void doTile(final ImageReader imageReader, final ZipOutputStream zout, boolean shouldApplyGammaCorrection)
throws IOException {
BufferedImage image = getTileOfFile(imageReader, 0, 0, getImageWidth(), getImageHeight(),
getOrientation(), getImageWidth(), getImageHeight());
final int zoomLevels = getZoomLevels(getImageWidth(), getImageHeight());
Expand All @@ -673,7 +662,7 @@ protected void doTile(final ImageReader imageReader, final ZipOutputStream zout)
}
}
if (z > 0) {
image = scaleBufferedImage(image);
image = scaleBufferedImage(image, shouldApplyGammaCorrection && zoomLevels == z);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ private static void stichTiles(final BufferedImage stitchImage, final BufferedIm
}

@Override
protected void doTile(final ImageReader imageReader, final ZipOutputStream zout) throws IOException {
protected void doTile(final ImageReader imageReader, final ZipOutputStream zout, boolean shouldApplyGammaCorrection)
throws IOException {
final int redWidth = (int) Math.ceil(getImageWidth() / ((double) megaTileSize / TILE_SIZE));
final int redHeight = (int) Math.ceil(getImageHeight() / ((double) megaTileSize / TILE_SIZE));
if (LOGGER.isDebugEnabled()) {
Expand Down Expand Up @@ -109,16 +110,16 @@ protected void doTile(final ImageReader imageReader, final ZipOutputStream zout)
LOGGER.debug("megaTile create - start tiling");
// stitch
final BufferedImage tile = writeTiles(zout, megaTile, x, y, imageZoomLevels, zoomFactor,
stopOnZoomLevel);
stopOnZoomLevel, shouldApplyGammaCorrection);
if (lastPhaseNeeded) {
stichTiles(lastPhaseImage, tile, x * TILE_SIZE, y * TILE_SIZE);
}
}
}
if (lastPhaseNeeded) {
lastPhaseImage = scaleBufferedImage(lastPhaseImage);
lastPhaseImage = scaleBufferedImage(lastPhaseImage, false);
final int lastPhaseZoomLevels = getZoomLevels(lastPhaseImage.getHeight(), lastPhaseImage.getWidth());
writeTiles(zout, lastPhaseImage, 0, 0, lastPhaseZoomLevels, 0, 0);
writeTiles(zout, lastPhaseImage, 0, 0, lastPhaseZoomLevels, 0, 0, false);
}
}

Expand All @@ -136,7 +137,8 @@ private void setZoomLevelPerStep(final short zoomLevel) {
}

private BufferedImage writeTiles(final ZipOutputStream zout, final BufferedImage megaTile, final int x,
final int y, final int imageZoomLevels, final int zoomFactor, final int stopOnZoomLevel) throws IOException {
final int y, final int imageZoomLevels, final int zoomFactor, final int stopOnZoomLevel, boolean flag)
throws IOException {
final int tWidth = megaTile.getWidth();
final int tHeight = megaTile.getHeight();
BufferedImage tile = null;
Expand All @@ -151,8 +153,8 @@ private BufferedImage writeTiles(final ZipOutputStream zout, final BufferedImage
}
}
if (imageZoomLevels > stopOnZoomLevel) {
tile = scaleBufferedImage(megaTile);
return writeTiles(zout, tile, x, y, imageZoomLevels - 1, zoomFactor / 2, stopOnZoomLevel);
tile = scaleBufferedImage(megaTile, flag);
return writeTiles(zout, tile, x, y, imageZoomLevels - 1, zoomFactor / 2, stopOnZoomLevel, false);
}
return tile;
}
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/org/mycore/imagetiler/internal/MCRTileDebugger.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.mycore.imagetiler.internal;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.util.Optional;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class MCRTileDebugger {

public static void debugImage(final String name, BufferedImage debugImage) {
if (!Optional.ofNullable(System.getProperty(MCRTileDebugger.class.getName()))
.map(Boolean::parseBoolean).orElse(false)) {
return;
}
SwingUtilities.invokeLater(() -> {
BufferedImage image = debugImage;
JFrame frame = new JFrame(name);

// Get the size of the screen
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

// Determine the new dimensions if the image is larger than the screen
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
if (imageWidth > screenSize.width || imageHeight > screenSize.height) {
double widthScale = (double) screenSize.width / imageWidth;
double heightScale = (double) screenSize.height / imageHeight;
double scale = Math.min(widthScale, heightScale);
imageWidth = (int) (imageWidth * scale);
imageHeight = (int) (imageHeight * scale);

Image scaledImage = image.getScaledInstance(imageWidth, imageHeight, Image.SCALE_SMOOTH);
image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g2d = image.createGraphics();
g2d.drawImage(scaledImage, 0, 0, null);
g2d.dispose();
}

JLabel imageLabel = new JLabel(new ImageIcon(image));
frame.add(imageLabel);

frame.pack();
frame.setLocationRelativeTo(null); // Center the frame
frame.setVisible(true);
});

}

}
Loading
Loading