Skip to content

Commit

Permalink
fix gamma correction for gray tiffs
Browse files Browse the repository at this point in the history
  • Loading branch information
yagee-de committed Aug 6, 2024
1 parent 52fb4ad commit 56a4f8c
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 42 deletions.
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

0 comments on commit 56a4f8c

Please sign in to comment.