Color differences between on-screen and saved image (Mac)

Similar to this, I am experiencing color consistency problems on my M1 Macbook Pro using the latest version of Processing.

Image looks vibrant on screen. If I do a screen grab within the OS then the colors are maintained when the screengrab image is opened Gimp.

If I use save() to save the Processing window image to .png, .bmp OR .tif it loses some of its vibrancy when the saved file is opened in Gimp. The colors no longer ‘pop’ - It looks like a loss of saturation but is not correctable in post by increasing this setting.

The linked issue suggests it’s to do with color profiles - how do I prevent this from happening? When I create something I want to save, I need the saved result to be identical to the onscreen image. Screen capture is limiting in terms of resolution.

Any advice would be warmly welcomed

Thanks

Edit: I’m currently at work so am unable to add actual code, full version numbers or link to images that show this issue. I’ll add this detail tonight. Thanks for your patience.

Find out which color profile your mac uses, download its .icc file, then use this code:

/**
 * Saves the current frame as an image file with the specified filename and
 * converts its color space to the given ICC profile.
 *
 * @param filename The name of the file to save the current frame to, including
 *                 the file extension (e.g., "example.png").
 *                 "example-srgb.png").
 * @throws IOException If an I/O error occurs while reading the ICC profile or
 *                     reading/writing the image files.
 */
@Override
public void save(String filename) {
	super.save(filename);

	ICC_Profile profile = null;
	try {
		profile = ICC_Profile.getInstance("srgb.icc"); // NOTE load profile of your choosing from file
	} catch (IOException e) {
		e.printStackTrace();
	}

	// Create the ICC_ColorSpace object
	ICC_ColorSpace colorSpace = new ICC_ColorSpace(profile);

	// Read the input image
	BufferedImage inputImage = null;
	try {
		inputImage = ImageIO.read(new File(filename));
	} catch (IOException e) {
		e.printStackTrace();
	}

	ColorConvertOp colorConvert = new ColorConvertOp(colorSpace, null);

	// Apply the color space conversion to the image
	BufferedImage outputImage = colorConvert.filter(inputImage, null);

	// Write the output image
	try {
		int dot = filename.lastIndexOf('.');
		String outputPath = filename.substring(0, dot) + "-srgb" + "." + filename.substring(dot + 1);
		ImageIO.write(outputImage, "PNG", new File(outputPath));
	} catch (IOException e) {
		e.printStackTrace();
	}
}
1 Like

Thanks @micycle - I’ll give this a try tonight.

@micyle I got rid of all of the red underlines by adding the following:

import java.awt.color.ICC_Profile;
import java.awt.color.ICC_ColorSpace;
import java.awt.image.ColorConvertOp;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

I have found the icc file, renamed it to ‘lcd.icc’ and added it to the sketch folder (and also a subfolder called ‘data’ just in case) but get the following error…

java.io.IOException: Cannot open file Color LCD-37D8832A-2D66-02CA-B9F7-8F30A301B230.icc
	at java.desktop/java.awt.color.ICC_Profile.getInstance(ICC_Profile.java:888)
	at sketch_70sArches_010.save(sketch_70sArches_010.java:260)
	at sketch_70sArches_010.keyPressed(sketch_70sArches_010.java:235)
	at processing.core.PApplet.keyPressed(PApplet.java:2772)
	at processing.core.PApplet.handleKeyEvent(PApplet.java:2636)
	at processing.core.PApplet.dequeueEvents(PApplet.java:2262)
	at processing.core.PApplet.handleDraw(PApplet.java:2104)
	at processing.awt.PSurfaceAWT$9.callDraw(PSurfaceAWT.java:1388)
	at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:356)
NullPointerException

I set permissions on the fie to everyone read and write. Still no joy. Any ideas?

Thanks

@micycle I changed the file load line to this…

profile = ICC_Profile.getInstance(sketchPath() + "/lcd.icc"); // NOTE load profile of your choosing from file

Now I get a different ‘can’t read’ error (progress!!):

javax.imageio.IIOException: Can't read input file!
	at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1310)
	at sketch_70sArches_010.save(sketch_70sArches_010.java:269)
	at sketch_70sArches_010.keyPressed(sketch_70sArches_010.java:234)
	at processing.core.PApplet.keyPressed(PApplet.java:2772)
	at processing.core.PApplet.handleKeyEvent(PApplet.java:2636)
	at processing.core.PApplet.dequeueEvents(PApplet.java:2262)
	at processing.core.PApplet.handleDraw(PApplet.java:2104)
	at processing.awt.PSurfaceAWT$9.callDraw(PSurfaceAWT.java:1388)
	at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:356)
NullPointerException

@micycle I fixed it!

Needed to pass the full rather than local file path to ‘save’:

save(sketchPath() + "/" + filename);```

To summarise - in case I or someone else comes back here, the following combo fixes the issue AND creates lovely, vivid output!!!

Main code:

save(sketchPath() + "/" + filename);

New Save routine

import java.awt.color.*;
import java.awt.image.*;
import javax.imageio.*;

/**
 * Saves the current frame as an image file with the specified filename and
 * converts its color space to the given ICC profile.
 *
 * @param filename The name of the file to save the current frame to, including
 *                 the file extension (e.g., "example.png").
 *                 "example-srgb.png").
 * @throws IOException If an I/O error occurs while reading the ICC profile or
 *                     reading/writing the image files.
 */
 
@Override
public void save(String filename) {
  super.save(filename);

  ICC_Profile profile = null;
  try {
    profile = ICC_Profile.getInstance(sketchPath() + "/lcd.icc"); // NOTE load profile of your choosing from file
  } catch (IOException e) {
    e.printStackTrace();
  }

  // Create the ICC_ColorSpace object
  ICC_ColorSpace colorSpace = new ICC_ColorSpace(profile);

  // Read the input image
  BufferedImage inputImage = null;
  try {
    inputImage = ImageIO.read(new File(filename));
  } catch (IOException e) {
    e.printStackTrace();
  }

  ColorConvertOp colorConvert = new ColorConvertOp(colorSpace, null);

  // Apply the color space conversion to the image
  BufferedImage outputImage = colorConvert.filter(inputImage, null);

  // Write the output image
  try {
    int dot = filename.lastIndexOf('.');
    String outputPath = filename.substring(0, dot) + "-srgb" + "." + filename.substring(dot + 1);
    ImageIO.write(outputImage, "PNG", new File(outputPath));
    println("Written");
  } catch (IOException e) {
    e.printStackTrace();
  }
}

Thanks for your help

@micycle I spoke too soon :frowning:

the saved imaged is actually darker than the result I was getting before implementing this change (see pic)