The Quest for the Perfect Palette Generator

A place for your artistic side. Discuss techniques and tools for pixel art on the NES, GBC, or similar platforms.

Moderator: Moderators

Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

The Quest for the Perfect Palette Generator

Post by Drag »

Update (11/3/2018):
Now available on GitHub.

------
Since I recently had to update my html5 nes palette generator to make newer versions of Firefox accept decimal values in the number fields, I kinda pondered over this problem again.

Right now, I've hit a wall. I cannot go any further, and googling turns up absolutely zero help, and after a year and a half, I've forgotten about any suggestions anyone told me, so here's what I know so far in hopes that maybe some day I might be able to solve this problem either directly or indirectly:

Televisions take a YIQ color signal. Y represents luminance (the black and white portion of the color), and IQ represent the hue and chroma (I'm not keen on how "chroma" is different from "saturation" but that's aside the point), which is then added on top of the black and white.

I made the assumption that the CRT television takes the YIQ information and converts it to RGB. This is not sRGB (what your computer display uses) however, but rather "how much energy to hit the red phosphor, the green phosphor, and the blue phosphor with". I also made the assumption that the television clamps negative R, G, and B values to 0, but doesn't clamp positive values to anything specific, probably just analog voltage limits.

This means that a CRT television taking in an analog signal has the capability of displaying colors that are not displayable on a computer monitor. For the NES specifically, these are many of the bright blue colors. You've probably seen the sky in SMB represented by a myriad of different bluish and purplish colors. The truth is, none of those colors are correct because that blue is actually one of the out-of-range colors I just mentioned.

I tried a bunch of different methods to generate a palette. The one I came up with that almost works is to partially convert the YIQ to RGB. Basically you take the YIQ->RGB matrix and remove Y from it. In my code, I call this tR, tG, and tB for some reason. Then, using CIELuv, I take the NTSC CIE definitions of what color the red, green, and blue phosphors are supposed to be, and treat them as vectors pointing away from the white point (D65 is the white point I'm using). I then multiply these vectors by tR, tG, and tB, and then I add them together, which gives me a point that represents the final color, which I then convert from CIELuv to CIEXYZ (this is where I use the Y that I didn't add to tR, tG, and tB), and from CIEXYZ to sRGB. If select the CIELuv option in my palette generator, you can see the result of this.

It's pretty close, but here's the wall I keep running into. I need to apply gamma correction, and it's not as simple as you'd think. Darker colors mix differently from brighter colors: the colors shift towards primary colors, so they barely mix at all. In my palette generator, the top row is supposed to look like three blues (just straight blue, the only difference is lightness, not hue), then magenta, then three reds (same deal as the blues), then super-dark brown (darkest non-black color in the palette actually), then three greens (again, straight greens, only difference is lightness), and then blue again, and the reason is because that's how it looks on my CRT TV. What you actually see in my palette generator is the full rainbow, which is not what I just described.

I absolutely cannot get this to work, so I figured I should try out a new method: I was going to convert YIQ completely to RGB, then mix R, G, and B in CIELuv space, using NTSC's definitions of red, green, blue, and white, and then convert that to CIEXYZ and then sRGB. When I tried this, the palette was skewed badly, so I probably didn't mix the colors right.

CIELuv is attractive because it's a linearized CIEXYZ. That means, if you pick two colors on the CIELuv map, and draw a straight line between them, the line represents all the colors you can create if you have two colored lights of those two colors. If you have three colored lights, I'm not quite sure what to do, because that makes a triangle, but the center point of the triangle is not necessarily white, which is why you have to use a white point, and a white point just means "what color should be created if all three lights have equal power?"

Though, in this case, my lights are actually phosphors being hit with an electron gun. I don't know if phosphors change color when they get brighter, which would be something to consider. However, this seems to be the way to go because I can gamma correct the R, G, and B signal, which simulates the gamma curve the phosphors have, and by mixing them in CIELuv space, I can simulate how our eyes percieve these colors when they mix on the TV screen. However, I don't know how to properly mix three colors together in CIELuv space, and even if I got it, I don't know if adding the gamma correction would solve the problem with the darker colors not being correct.

And that's why I gave up way back then; I simply do not have enough information to continue, and obtaining said information seems to be more of a chore than I have the will for, since I seem to be the only person on Earth trying to do this. :\
Last edited by Drag on Sat Nov 03, 2018 7:28 pm, edited 1 time in total.
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: The Quest for the Perfect Palette Generator

Post by lidnariq »

Two things that might help:

* The vast majority of phosphors don't change wavelength as a function of radiation intensity (especially not the ones standardized in televisions; wikipedia says they use(d?) the P22 trio).

* The gamma correction done by the television was actually an intrinsic property of the amplifier and voltage on the control grid changing the electron current density. (This is basically a triode vacuum tube with extra stuff afterwards). As such, we're doomed to an eternity of gamma correcting things with a gamma of 2.2 or so because including the gamma correction in the end users' sets in 1950 would have made them even more ludicrously expensive.
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: The Quest for the Perfect Palette Generator

Post by Drag »

Thanks, knowing that the color primaries are constant removes a bit of headache.

Also, I'm pretty sure I have it right, but just to double check, the 2.2 gamma is the curve that the phosphors have, correct? I.e., an actual "gamma-corrected" broadcast uses a gamma of 1/2.2 to compensate, right?
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: The Quest for the Perfect Palette Generator

Post by lidnariq »

Drag wrote:the 2.2 gamma is the curve that the phosphors have, correct?
Not just the phosphors. It's also a function of how CRTs work. ( https://en.wikipedia.org/wiki/File:Trio ... istic1.png )

I'm under the impression that the phosphors themselves are a mostly-linear "N electrons go in, N÷k photons come out".
I.e., an actual "gamma-corrected" broadcast uses a gamma of 1/2.2 to compensate, right?
Yes, this is true.
Joe
Posts: 649
Joined: Mon Apr 01, 2013 11:17 pm

Re: The Quest for the Perfect Palette Generator

Post by Joe »

I feel like you're making this more complicated than it needs to be.

Conversion from YUV/YIQ to the TV-standard RGB is extremely simple and easy to look up. The only catch is that, in the NES, Y, R, G, and B can all be negative. (For your purposes, YUV and YIQ are interchangeable.)

I think all you need to do from there is convert from TV RGB to sRGB using the equations provided in this book.
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: The Quest for the Perfect Palette Generator

Post by Drag »

That's a cool book that I might actually get at some point, thanks. :D

Well, the reason I wanted to mix R, G, and B in CIELuv space is because the NTSC's definition of RGB is different from sRGB. Therefore, colors combine differently when you're using sRGB vs NTSC. CIELuv space is as low-level as I can get; I literally am emulating 3 colors of phosphors (red, green, and blue as defined by NTSC) whose light is mixing the way our eyes see them, and then I'm converting them back to sRGB. Converting YIQ directly to RGB produces an okay palette, but when I started using CIELuv, the colors looked a LOT closer to what my TV produces. You could argue up and down to me that it doesn't matter, but my eyes disagree. :P

Anyway, I have the color mixing working now (I indeed was mixing incorrectly), but I have one more problem.

R, G, and B are the YIQ -> RGB values, negative numbers clamped to 0, positive numbers unclamped.
Rp, Gp, and Bp are the red, green, and blue primaries as defined by the NTSC.

Here's how I'm mixing:

Code: Select all

  R             G             B
-----Rp   +   -----Gp   +   -----Bp
R+G+B         R+G+B         R+G+B
I'm using that formula for the X coordinate and the Y coordinate.

So, RpGpBp is a triangle, and R, G, and B represent how close to Rp, Gp, or Bp the resulting color is. When R, G, and B are equal in value, the resulting color is in the exact center of the triangle. The problem is, the white point is not in the center of the triangle.

Fortunately, I have the math set up so that the white point is (0,0), which is a point that is off-center. I need to figure out how to add three colors in CIELuv space, while taking the white point into account.

When R, G, and B are equal, R/(R+G+B), G/(R+G+B), and B/(R+G+B) are all equal to 1/3. If I subtract 1/3 from there, I can shift the colors so that unsaturated colors indeed hit the white point, and not the center, and the resulting palette looks good, but I know this isn't the correct way to do white point correction.
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: The Quest for the Perfect Palette Generator

Post by Drag »

Just as a heads up, I've updated the palette generator to show off what I have so far.

The older version with the junky options I removed is still available here.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: The Quest for the Perfect Palette Generator

Post by rainwarrior »

Is there any way this could output a binary file (like FCEUX's palette files), or even just a more easily parsed text version? It would be nice if this was just a little easier to drop into an emulator to try out.
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: The Quest for the Perfect Palette Generator

Post by Drag »

I will once I can figure out how to do it in HTML5, and once I create a good default palette preset.

I'm still working on the white point correction. Instead of subtracting (1/3) from the normalized R, G, and B, I can apply a gamma curve to them, and if I manually tweak it, I can kinda "bend" the inside of the triangle so the midpoint is where I want it, but I still don't know if this is the correct way to perform an additive mix on 3 colors in CIELuv with respect to a white point.
JRoatch
Formerly 43110
Posts: 422
Joined: Wed Feb 05, 2014 7:01 am
Contact:

Re: The Quest for the Perfect Palette Generator

Post by JRoatch »

Drag wrote:I will once I can figure out how to do it in HTML5
have a link element with a download attribute containing the file name and the href attribute formated as a data uri for the file data.
Joe
Posts: 649
Joined: Mon Apr 01, 2013 11:17 pm

Re: The Quest for the Perfect Palette Generator

Post by Joe »

Drag wrote:Well, the reason I wanted to mix R, G, and B in CIELuv space is because the NTSC's definition of RGB is different from sRGB.
That's why I linked directly to the two pages in the book that describe converting between NTSC's RGB and sRGB, using CIEXYZ (which is one mathematical transform away from CIELuv). Hopefully you can see them okay, it looks like Google has started hiding some pages.

I don't think you need to correct for the white point at all - if NTSC and sRGB specify different white points, then shouldn't "white" in NTSC be different from "white" in sRGB?
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: The Quest for the Perfect Palette Generator

Post by Drag »

Joe wrote:
Drag wrote:Well, the reason I wanted to mix R, G, and B in CIELuv space is because the NTSC's definition of RGB is different from sRGB.
That's why I linked directly to the two pages in the book that describe converting between NTSC's RGB and sRGB, using CIEXYZ (which is one mathematical transform away from CIELuv). Hopefully you can see them okay, it looks like Google has started hiding some pages.

I don't think you need to correct for the white point at all - if NTSC and sRGB specify different white points, then shouldn't "white" in NTSC be different from "white" in sRGB?
Yeah, I started thinking about that. I was convinced that mixing the colors manually in CIELuv was the way to go, but after trying to solve this white point problem and seeing how you account for white points in RGB->XYZ matrices, I'm beginning to think that you're right. My new plan of attack later tonight will be to generate the RGB->XYZ matrix (so you can specify different white points and primaries), and run the YIQ->RGB output into that, and then convert CIEXYZ back to sRGB using a fixed matrix. The idea is still the same; generate the color using NTSC's definition of red, green, blue, and white, rather than sRGB's. I'll see if that gives me any better results.

At some point, I'll add the download link now that I know it's easy. :P What's FCEUX's palette file format again? 64 RGB definitions in palette order? Or is it BGR or something?
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: The Quest for the Perfect Palette Generator

Post by rainwarrior »

http://www.fceux.com/web/help/fceux.html?Palette.html
"Palette files are expected to contain 64 8-bit RGB triplets (each in that order; red comes first in the triplet in the file, then green, then blue)."
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: The Quest for the Perfect Palette Generator

Post by Drag »

I eventually figured out how to do the white point correction using my CIELuv method. I basically had to multiply R, G, and B by weights before the R/(R+G+B) etc calculations. The weights had to be between [0..1], and if I manually tweaked them, I could get the white point correct.

The only problem is that YIQ->RGB->CIEXYZ(ntsc)->sRGB produces identical results, with a lot less math and manual intervention. :P So you all were right, I was overcomplicating it by manually mixing the colors in CIELuv.

Ok, so what I have now is a palette generator, which generates the color in an RGB space defined by the CIExy points for the red, green, and blue primaries and the white point, and then converts it to sRGB so your computer can display it. If I can dig up the PAL definitions of RGBW and add YUV decoding, I can hypothetically create a PAL palette generator too, but I'd have no way to compare it against a real TV.

Anyway, I'm going to tidy my app's code up, and strip some now-unneeded stuff, and then I'll upload the updated version, in which I'm also planning to add a download button. :P
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: The Quest for the Perfect Palette Generator

Post by lidnariq »

There's some weirdness with exactly how the 2C07 generates colors, from my experiment (with thefox's help) over here.

I don't know how we're going to figure out better what's going on without a lot of time with a PAL NES and an oscilloscope, and possibly also changing the clock frequency from 26MHz to something slower.
Post Reply