From 533461204ad321ababcb7e4b8efc47e6c043003c Mon Sep 17 00:00:00 2001 From: jbb01 <32650546+jbb01@users.noreply.github.com> Date: Mon, 12 Aug 2024 13:13:39 +0200 Subject: [PATCH] properly implement transfer functions and apply them to the output image --- .../java/eu/jonahbauer/raytracing/Main.java | 4 +- .../raytracing/render/ImageFormat.java | 135 ------------------ .../raytracing/render/canvas/LiveCanvas.java | 2 +- .../raytracing/render/color/ColorRGB.java | 18 --- .../raytracing/render/color/ColorSpace.java | 22 ++- .../raytracing/render/color/ColorSpaces.java | 6 +- .../render/color/TransferFunction.java | 8 ++ .../render/color/TransferFunctions.java | 50 +++++++ .../raytracing/render/image/ImageWriter.java | 19 +++ .../render/image/PNGImageWriter.java | 107 ++++++++++++++ .../render/image/PPMImageWriter.java | 46 ++++++ .../render/texture/ImageTexture.java | 7 +- 12 files changed, 259 insertions(+), 165 deletions(-) delete mode 100644 src/main/java/eu/jonahbauer/raytracing/render/ImageFormat.java create mode 100644 src/main/java/eu/jonahbauer/raytracing/render/color/TransferFunction.java create mode 100644 src/main/java/eu/jonahbauer/raytracing/render/color/TransferFunctions.java create mode 100644 src/main/java/eu/jonahbauer/raytracing/render/image/ImageWriter.java create mode 100644 src/main/java/eu/jonahbauer/raytracing/render/image/PNGImageWriter.java create mode 100644 src/main/java/eu/jonahbauer/raytracing/render/image/PPMImageWriter.java diff --git a/src/main/java/eu/jonahbauer/raytracing/Main.java b/src/main/java/eu/jonahbauer/raytracing/Main.java index f8ee54b..52dad4e 100644 --- a/src/main/java/eu/jonahbauer/raytracing/Main.java +++ b/src/main/java/eu/jonahbauer/raytracing/Main.java @@ -1,9 +1,9 @@ package eu.jonahbauer.raytracing; -import eu.jonahbauer.raytracing.render.ImageFormat; import eu.jonahbauer.raytracing.render.canvas.Canvas; import eu.jonahbauer.raytracing.render.canvas.LiveCanvas; import eu.jonahbauer.raytracing.render.canvas.XYZCanvas; +import eu.jonahbauer.raytracing.render.image.PNGImageWriter; import eu.jonahbauer.raytracing.render.renderer.SimpleRenderer; import eu.jonahbauer.raytracing.render.color.ColorSpaces; import org.jetbrains.annotations.NotNull; @@ -42,7 +42,7 @@ public class Main { renderer.render(camera, scene, canvas); System.out.printf("rendering finished after %dms", (System.nanoTime() - time) / 1_000_000); - ImageFormat.PNG.write(canvas, config.path); + PNGImageWriter.sRGB.write(canvas, config.path); } private record Config(@NotNull Example example, @NotNull Path path, boolean preview, boolean iterative, boolean parallel, int samples, int depth) { diff --git a/src/main/java/eu/jonahbauer/raytracing/render/ImageFormat.java b/src/main/java/eu/jonahbauer/raytracing/render/ImageFormat.java deleted file mode 100644 index 2347c00..0000000 --- a/src/main/java/eu/jonahbauer/raytracing/render/ImageFormat.java +++ /dev/null @@ -1,135 +0,0 @@ -package eu.jonahbauer.raytracing.render; - -import eu.jonahbauer.raytracing.render.canvas.Canvas; -import eu.jonahbauer.raytracing.render.color.ColorSpaces; -import org.jetbrains.annotations.NotNull; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.zip.CRC32; -import java.util.zip.CheckedOutputStream; -import java.util.zip.DeflaterOutputStream; - -public enum ImageFormat { - PPM { - @Override - public void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException { - try (var writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.US_ASCII))) { - writer.write("P3\n"); - writer.write(String.valueOf(image.getWidth())); - writer.write(" "); - writer.write(String.valueOf(image.getHeight())); - writer.write("\n255\n"); - - for (int y = 0; y < image.getHeight(); y++) { - for (int x = 0; x < image.getWidth(); x++) { - var color = image.getRGB(x, y, ColorSpaces.sRGB); - writer.write(String.valueOf(color.red())); - writer.write(" "); - writer.write(String.valueOf(color.green())); - writer.write(" "); - writer.write(String.valueOf(color.blue())); - writer.write("\n"); - } - } - } - } - }, - PNG { - private static final byte[] MAGIC = new byte[] { (byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; - private static final int IHDR_LENGTH = 13; - private static final int IHDR_TYPE = 0x49484452; - private static final int IDAT_TYPE = 0x49444154; - private static final int IEND_TYPE = 0x49454E44; - private static final int IEND_CRC = 0xAE426082; - - @Override - public void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException { - try (var data = new NoCloseDataOutputStream(out); var _ = data.closeable()) { - data.write(MAGIC); - - writeIHDR(image, data); - writeIDAT(image, data); - writeIEND(image, data); - } - } - - private void writeIHDR(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException { - data.writeInt(IHDR_LENGTH); - try ( - var crc = new CheckedOutputStream(data, new CRC32()); - var ihdr = new DataOutputStream(crc) - ) { - ihdr.writeInt(IHDR_TYPE); - ihdr.writeInt(image.getWidth()); // image width - ihdr.writeInt(image.getHeight()); // image height - ihdr.writeByte(8); // bit depth - ihdr.writeByte(2); // color type - ihdr.writeByte(0); // compression method - ihdr.writeByte(0); // filter method - ihdr.writeByte(0); // interlace method - ihdr.flush(); - data.writeInt((int) crc.getChecksum().getValue()); - } - } - - private void writeIDAT(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException { - try ( - var baos = new ByteArrayOutputStream(); - var crc = new CheckedOutputStream(baos, new CRC32()); - var idat = new DataOutputStream(crc) - ) { - idat.writeInt(IDAT_TYPE); - - try (var deflate = new DataOutputStream(new DeflaterOutputStream(idat))) { - for (int y = 0; y < image.getHeight(); y++) { - deflate.writeByte(0); // filter type - for (int x = 0; x < image.getWidth(); x++) { - var pixel = image.getRGB(x, y, ColorSpaces.sRGB); - deflate.writeByte(pixel.red()); - deflate.writeByte(pixel.green()); - deflate.writeByte(pixel.blue()); - } - } - } - - var bytes = baos.toByteArray(); - data.writeInt(bytes.length - 4); // don't include type in length - data.write(bytes); - data.writeInt((int) crc.getChecksum().getValue()); - } - } - - private void writeIEND(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException { - data.writeInt(0); - data.writeInt(IEND_TYPE); - data.writeInt(IEND_CRC); - } - - private static class NoCloseDataOutputStream extends DataOutputStream { - public NoCloseDataOutputStream(OutputStream out) { - super(out); - } - - @Override - public void close() { - // do nothing - } - - public Closeable closeable() { - return super::close; - } - } - }, - ; - - public void write(@NotNull Canvas image, @NotNull Path path) throws IOException { - try (var out = Files.newOutputStream(path)) { - write(image, out); - } - } - - public abstract void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException; -} diff --git a/src/main/java/eu/jonahbauer/raytracing/render/canvas/LiveCanvas.java b/src/main/java/eu/jonahbauer/raytracing/render/canvas/LiveCanvas.java index 5075999..18b6a60 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/canvas/LiveCanvas.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/canvas/LiveCanvas.java @@ -36,7 +36,7 @@ public final class LiveCanvas implements Canvas { @Override public void add(int x, int y, int n, @NotNull SampledSpectrum spectrum, @NotNull SampledWavelengths lambda) { delegate.add(x, y, n, spectrum, lambda); - var color = ColorRGB.gamma(delegate.getRGB(x, y, cs)); + var color = cs.encode(delegate.getRGB(x, y, cs)); var rgb = color.red() << 16 | color.green() << 8 | color.blue(); image.setRGB(x, y, rgb); } diff --git a/src/main/java/eu/jonahbauer/raytracing/render/color/ColorRGB.java b/src/main/java/eu/jonahbauer/raytracing/render/color/ColorRGB.java index 012cbd4..9ee3fbf 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/color/ColorRGB.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/color/ColorRGB.java @@ -58,24 +58,6 @@ public record ColorRGB(double r, double g, double b) implements IVec3 ); } - public static @NotNull ColorRGB gamma(@NotNull ColorRGB color) { - return new ColorRGB(gamma(color.r), gamma(color.g), gamma(color.b)); - } - - public static @NotNull ColorRGB inverseGamma(@NotNull ColorRGB color) { - return new ColorRGB(inverseGamma(color.r), inverseGamma(color.g), inverseGamma(color.b)); - } - - private static double gamma(double value) { - if (value <= 0.0031308) return 12.92 * value; - return 1.055 * Math.pow(value, 1. / 2.4) - 0.055; - } - - private static double inverseGamma(double value) { - if (value <= 0.04045) return value / 12.92; - return Math.pow((value + 0.055) / 1.055, 2.4d); - } - @Override public @NotNull ColorRGB plus(@NotNull ColorRGB other) { return new ColorRGB(r + other.r, g + other.g, b + other.b); diff --git a/src/main/java/eu/jonahbauer/raytracing/render/color/ColorSpace.java b/src/main/java/eu/jonahbauer/raytracing/render/color/ColorSpace.java index 753d385..214ebe6 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/color/ColorSpace.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/color/ColorSpace.java @@ -25,16 +25,18 @@ public final class ColorSpace { private final @NotNull Matrix3 XYZfromRGB; private final @NotNull Matrix3 RGBfromXYZ; private final @NotNull SpectrumTable RGBtoSpectrumTable; + private final @NotNull TransferFunction transferFunction; public ColorSpace( @NotNull Chromaticity r, @NotNull Chromaticity g, @NotNull Chromaticity b, - @NotNull Spectrum illuminant, @NotNull SpectrumTable table + @NotNull Spectrum illuminant, @NotNull SpectrumTable table, @NotNull TransferFunction transferFunction ) { this.r = Objects.requireNonNull(r, "r"); this.g = Objects.requireNonNull(g, "g"); this.b = Objects.requireNonNull(b, "b"); this.illuminant = new DenselySampledSpectrum(illuminant); - this.RGBtoSpectrumTable = table; + this.RGBtoSpectrumTable = table; // no null-check + this.transferFunction = transferFunction; // no null-check this.W = illuminant.toXYZ(); this.w = W.xy(); @@ -53,6 +55,10 @@ public final class ColorSpace { this.RGBfromXYZ = XYZfromRGB.invert(); } + /* + * Conversions + */ + public @NotNull ColorRGB toRGB(@NotNull ColorXYZ xyz) { var out = RGBfromXYZ.times(xyz.toVec3()); return new ColorRGB(out.x(), out.y(), out.z()); @@ -84,6 +90,18 @@ public final class ColorSpace { } } + public @NotNull ColorRGB encode(@NotNull ColorRGB rgb) { + return transferFunction.encode(rgb); + } + + public @NotNull ColorRGB decode(@NotNull ColorRGB rgb) { + return transferFunction.decode(rgb); + } + + /* + * Spectrum + */ + public @NotNull SigmoidPolynomial toPolynomial(@NotNull ColorRGB rgb) { return RGBtoSpectrumTable.get(new ColorRGB( Math.max(0, rgb.r()), diff --git a/src/main/java/eu/jonahbauer/raytracing/render/color/ColorSpaces.java b/src/main/java/eu/jonahbauer/raytracing/render/color/ColorSpaces.java index a2742df..ff4718e 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/color/ColorSpaces.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/color/ColorSpaces.java @@ -13,21 +13,21 @@ public final class ColorSpaces { new Chromaticity(0.6400, 0.3300), new Chromaticity(0.3000, 0.6000), new Chromaticity(0.1500, 0.0600), - Spectra.D65, read("sRGB_spectrum.bin") + Spectra.D65, read("sRGB_spectrum.bin"), TransferFunctions.sRGB ); // P3-D65 (display) public static final @NotNull ColorSpace DCI_P3 = new ColorSpace( new Chromaticity(0.680, 0.320), new Chromaticity(0.265, 0.690), new Chromaticity(0.150, 0.060), - Spectra.D65, read("DCI_P3_spectrum.bin") + Spectra.D65, read("DCI_P3_spectrum.bin"), TransferFunctions.sRGB ); // ITU-R Rec BT.2020 public static final @NotNull ColorSpace Rec2020 = new ColorSpace( new Chromaticity(0.708, 0.292), new Chromaticity(0.170, 0.797), new Chromaticity(0.131, 0.046), - Spectra.D65, read("Rec2020_spectrum.bin") + Spectra.D65, read("Rec2020_spectrum.bin"), null ); private static @NotNull SpectrumTable read(@NotNull String name) { diff --git a/src/main/java/eu/jonahbauer/raytracing/render/color/TransferFunction.java b/src/main/java/eu/jonahbauer/raytracing/render/color/TransferFunction.java new file mode 100644 index 0000000..552ae75 --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/render/color/TransferFunction.java @@ -0,0 +1,8 @@ +package eu.jonahbauer.raytracing.render.color; + +import org.jetbrains.annotations.NotNull; + +public interface TransferFunction { + @NotNull ColorRGB decode(@NotNull ColorRGB rgb); + @NotNull ColorRGB encode(@NotNull ColorRGB rgb); +} diff --git a/src/main/java/eu/jonahbauer/raytracing/render/color/TransferFunctions.java b/src/main/java/eu/jonahbauer/raytracing/render/color/TransferFunctions.java new file mode 100644 index 0000000..581a20d --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/render/color/TransferFunctions.java @@ -0,0 +1,50 @@ +package eu.jonahbauer.raytracing.render.color; + +import org.jetbrains.annotations.NotNull; + +public final class TransferFunctions { + public static final @NotNull TransferFunction sRGB = new ComponentTransferFunction() { + @Override + protected double encode(double value) { + if (value <= 0.0031308) return 12.92 * value; + return 1.055 * Math.pow(value, 1. / 2.4) - 0.055; + } + + @Override + protected double decode(double value) { + if (value <= 0.04045) return value / 12.92; + return Math.pow((value + 0.055) / 1.055, 2.4d); + } + }; + + public static final @NotNull TransferFunction LINEAR = new TransferFunction() { + @Override + public @NotNull ColorRGB encode(@NotNull ColorRGB rgb) { + return rgb; + } + + @Override + public @NotNull ColorRGB decode(@NotNull ColorRGB rgb) { + return rgb; + } + }; + + private TransferFunctions() { + throw new UnsupportedOperationException(); + } + + private abstract static class ComponentTransferFunction implements TransferFunction { + @Override + public final @NotNull ColorRGB decode(@NotNull ColorRGB rgb) { + return new ColorRGB(decode(rgb.r()), decode(rgb.g()), decode(rgb.b())); + } + + @Override + public final @NotNull ColorRGB encode(@NotNull ColorRGB rgb) { + return new ColorRGB(encode(rgb.r()), encode(rgb.g()), encode(rgb.b())); + } + + protected abstract double encode(double value); + protected abstract double decode(double value); + } +} diff --git a/src/main/java/eu/jonahbauer/raytracing/render/image/ImageWriter.java b/src/main/java/eu/jonahbauer/raytracing/render/image/ImageWriter.java new file mode 100644 index 0000000..f735084 --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/render/image/ImageWriter.java @@ -0,0 +1,19 @@ +package eu.jonahbauer.raytracing.render.image; + +import eu.jonahbauer.raytracing.render.canvas.Canvas; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +public interface ImageWriter { + default void write(@NotNull Canvas image, @NotNull Path path) throws IOException { + try (var out = Files.newOutputStream(path)) { + write(image, out); + } + } + + void write(@NotNull Canvas canvas, @NotNull OutputStream out) throws IOException; +} diff --git a/src/main/java/eu/jonahbauer/raytracing/render/image/PNGImageWriter.java b/src/main/java/eu/jonahbauer/raytracing/render/image/PNGImageWriter.java new file mode 100644 index 0000000..0b0125e --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/render/image/PNGImageWriter.java @@ -0,0 +1,107 @@ +package eu.jonahbauer.raytracing.render.image; + +import eu.jonahbauer.raytracing.render.canvas.Canvas; +import eu.jonahbauer.raytracing.render.color.ColorSpace; +import eu.jonahbauer.raytracing.render.color.ColorSpaces; +import org.jetbrains.annotations.NotNull; + +import java.io.*; +import java.util.Objects; +import java.util.zip.CRC32; +import java.util.zip.CheckedOutputStream; +import java.util.zip.DeflaterOutputStream; + +public class PNGImageWriter implements ImageWriter { + public static final @NotNull PNGImageWriter sRGB = new PNGImageWriter(ColorSpaces.sRGB); + + private static final byte[] MAGIC = new byte[] { (byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; + private static final int IHDR_LENGTH = 13; + private static final int IHDR_TYPE = 0x49484452; + private static final int IDAT_TYPE = 0x49444154; + private static final int IEND_TYPE = 0x49454E44; + private static final int IEND_CRC = 0xAE426082; + + private final @NotNull ColorSpace cs; + + public PNGImageWriter(@NotNull ColorSpace cs) { + this.cs = Objects.requireNonNull(cs, "cs"); + } + + @Override + public void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException { + try (var data = new NoCloseDataOutputStream(out); var _ = data.closeable()) { + data.write(MAGIC); + + writeIHDR(image, data); + writeIDAT(image, data); + writeIEND(image, data); + } + } + + private void writeIHDR(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException { + data.writeInt(IHDR_LENGTH); + try ( + var crc = new CheckedOutputStream(data, new CRC32()); + var ihdr = new DataOutputStream(crc) + ) { + ihdr.writeInt(IHDR_TYPE); + ihdr.writeInt(image.getWidth()); // image width + ihdr.writeInt(image.getHeight()); // image height + ihdr.writeByte(8); // bit depth + ihdr.writeByte(2); // color type + ihdr.writeByte(0); // compression method + ihdr.writeByte(0); // filter method + ihdr.writeByte(0); // interlace method + ihdr.flush(); + data.writeInt((int) crc.getChecksum().getValue()); + } + } + + private void writeIDAT(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException { + try ( + var baos = new ByteArrayOutputStream(); + var crc = new CheckedOutputStream(baos, new CRC32()); + var idat = new DataOutputStream(crc) + ) { + idat.writeInt(IDAT_TYPE); + + try (var deflate = new DataOutputStream(new DeflaterOutputStream(idat))) { + for (int y = 0; y < image.getHeight(); y++) { + deflate.writeByte(0); // filter type + for (int x = 0; x < image.getWidth(); x++) { + var pixel = cs.encode(image.getRGB(x, y, cs)); + deflate.writeByte(pixel.red()); + deflate.writeByte(pixel.green()); + deflate.writeByte(pixel.blue()); + } + } + } + + var bytes = baos.toByteArray(); + data.writeInt(bytes.length - 4); // don't include type in length + data.write(bytes); + data.writeInt((int) crc.getChecksum().getValue()); + } + } + + private void writeIEND(@NotNull Canvas image, @NotNull DataOutputStream data) throws IOException { + data.writeInt(0); + data.writeInt(IEND_TYPE); + data.writeInt(IEND_CRC); + } + + private static class NoCloseDataOutputStream extends DataOutputStream { + public NoCloseDataOutputStream(OutputStream out) { + super(out); + } + + @Override + public void close() { + // do nothing + } + + public Closeable closeable() { + return super::close; + } + } +} diff --git a/src/main/java/eu/jonahbauer/raytracing/render/image/PPMImageWriter.java b/src/main/java/eu/jonahbauer/raytracing/render/image/PPMImageWriter.java new file mode 100644 index 0000000..fbbcd14 --- /dev/null +++ b/src/main/java/eu/jonahbauer/raytracing/render/image/PPMImageWriter.java @@ -0,0 +1,46 @@ +package eu.jonahbauer.raytracing.render.image; + +import eu.jonahbauer.raytracing.render.canvas.Canvas; +import eu.jonahbauer.raytracing.render.color.ColorSpace; +import eu.jonahbauer.raytracing.render.color.ColorSpaces; +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +public class PPMImageWriter implements ImageWriter { + public static final PPMImageWriter sRGB = new PPMImageWriter(ColorSpaces.sRGB); + + private final @NotNull ColorSpace cs; + + public PPMImageWriter(@NotNull ColorSpace cs) { + this.cs = Objects.requireNonNull(cs, "cs"); + } + + @Override + public void write(@NotNull Canvas image, @NotNull OutputStream out) throws IOException { + try (var writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.US_ASCII))) { + writer.write("P3\n"); + writer.write(String.valueOf(image.getWidth())); + writer.write(" "); + writer.write(String.valueOf(image.getHeight())); + writer.write("\n255\n"); + + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + var color = cs.encode(image.getRGB(x, y, cs)); + writer.write(String.valueOf(color.red())); + writer.write(" "); + writer.write(String.valueOf(color.green())); + writer.write(" "); + writer.write(String.valueOf(color.blue())); + writer.write("\n"); + } + } + } + } +} diff --git a/src/main/java/eu/jonahbauer/raytracing/render/texture/ImageTexture.java b/src/main/java/eu/jonahbauer/raytracing/render/texture/ImageTexture.java index c4b9db8..126bb06 100644 --- a/src/main/java/eu/jonahbauer/raytracing/render/texture/ImageTexture.java +++ b/src/main/java/eu/jonahbauer/raytracing/render/texture/ImageTexture.java @@ -19,22 +19,21 @@ public final class ImageTexture implements Texture { private final int height; private final @NotNull Spectrum[][] spectra; - public ImageTexture(@NotNull BufferedImage image, @NotNull ColorSpace cs, @NotNull Spectrum.Type type, boolean gamma) { + public ImageTexture(@NotNull BufferedImage image, @NotNull ColorSpace cs, @NotNull Spectrum.Type type) { this.width = image.getWidth(); this.height = image.getHeight(); this.spectra = new Spectrum[height][width]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - var rgb = new ColorRGB(image.getRGB(x, y)); - if (gamma) rgb = ColorRGB.inverseGamma(rgb); + var rgb = cs.decode(new ColorRGB(image.getRGB(x, y))); spectra[y][x] = cs.toSpectrum(rgb, type); } } } public ImageTexture(@NotNull String path, @NotNull ColorSpace cs) { - this(read(path), cs, Spectrum.Type.ALBEDO, true); + this(read(path), cs, Spectrum.Type.ALBEDO); } private static @NotNull BufferedImage read(@NotNull String path) {