I wrote a Python script that fuses a stereo pair of images into an anaglyph image. When I view the image in a tkinter application I wrote the image looks good but when I save as a JPG and PNG to Photoshop it looks “confused”. For a while I was wondering if I was crazy but I had my family look at the images and they agreed.
I figured the problem had something to do with the colors being screwed up and I figured I could prove it by making a simpler image so I made something that looks like a TV test pattern. Here’s the smoking gun:
screenshot that shows the problem
On the left is the tkinter application showing the color bars, on the right I am viewing the image exported by the application, which I’m going to call A.PNG, in the Windows Photo Viewer.
The green is obviously different, and if I load the screenshot (B.PNG) into Photoshop I see the yellow bar on the left image is (180, 180, 16) which is what it supposed to be, but the photo viewer shows (176,178,41) for the yellow. If I use the eyedropper on A.PNG the yellow is (180, 180, 16) so the problem is that when the image gets loaded by other applications the color displayed is changed to something else.
I’m pretty sure this has something do with color management or gamma correction but I’m at a loss at what to do to my image so I can view it with other programs or print it out and have the colors look like they do in tkinter.
Here is the script that generates the color bars, writes them to a file and writes to a PNG
from math import floor from tkinter import Tk, Label from PIL.Image import new from PIL import ImageDraw, ImageTk _480p=(640,480) WHITE75 = (180,180,180) YELLOW75 = (180,180,16) CYAN75= (16,180,180) GREEN75= (16,180,16) MAGENTA75 = (180,16,180) RED75 = (180,16,16) BLUE75 = (16,16,180) BLACK75 = (16,16,16) RAINBOW75 = [WHITE75, YELLOW75, CYAN75, GREEN75, MAGENTA75, RED75, BLUE75] LOWER75 = [BLUE75, BLACK75, MAGENTA75, BLACK75, CYAN75, BLACK75, WHITE75] GRAYS = [(16*i,16*i,16*i) for i in range(1,15)] REDS = [(16*i,0,0) for i in reversed(range(1,15))] GREENS = [(0,16*i,0) for i in range(1,15)] BLUES = [(0,0,16*i) for i in reversed(range(1,15))] class TestPattern(): def draw(self): self.x = new("RGB",_480p,"black") self.bars(RAINBOW75,0,320) self.bars(LOWER75,320,360) self.bars(GRAYS,360,420) self.bars(REDS,420,440) self.bars(GREENS,440,460) self.bars(BLUES,460,480) return self.x def bars(self,colors,top,bottom): draw = ImageDraw.Draw(self.x) barwidth = 640 / len(colors) for i in range(len(colors)): draw.rectangle([floor(i*barwidth),top,floor((i+1)*barwidth),bottom], fill=colors[i]) root = Tk() root.title("Image Loader") root.geometry(f"640x480+300+150") root.resizable(width=True, height=True) pattern = TestPattern().draw() pattern.save(r"D:test-pattern.png") tkimg = ImageTk.PhotoImage(pattern) panel = Label(root, image=tkimg) panel.image = tkimg panel.grid(row=1, column=1) root.mainloop() curve=[23,37,51,66,80,96,111,126,142,158,174,190,206,222]
Advertisement
Answer
Here’s what I figured out.
There was nothing significantly different in the JPG and PNG files I was making with Pillow from files saved by Photoshop and Snagit, which I was using to make screenshots. (I wrote some Python scripts that removed metadata such as the ICC Profiles from the images)
My monitor however is a Dell U2711 which is a wide gamut monitor. Applications like Photoshop, Windows Photo Viewer and the Firefox web browser apply a color profile for the monitor, so anything you capture in a screenshot has been transformed to the color space of the monitor. (180, 180, 16) gets transformed to (176,178,41) in the screenshot. Tkinter is an ancient application that isn’t color managed so you ask for (180, 180, 180) and you really get that in the screenshot. When I took screenshots on the built-in monitor on my laptop I found that colors in the screenshot agreed with the eyedropper tool in Photoshop and they were the same in tkinter and Photoshop.