Post changed to reflect the results of the discussion bellow with zoyd and JohnAd.
I would like to thank zoyd for testing my previous solution, with the same results as mine, and helping me find the error. I also would like to thank JohnAd for suggesting the use of mplayerc and its shader support for performing the color correction. He has also wrote the shader code presented here for performing the color correction, so a big part of the credits should go to him. Finally, I would like to thank to the authors of the color articles I point to.
I have been working to find a way for correcting the image of a display with oversaturated primaries using a HTPC. We will try to correct the image to have the colors as close as possible to the sourcing standard, within our display's color gamut. For this we will use mplayerc (Media Player Classic) which is available to anyone for free.
Before I start, I would like to tell you that I am not an expert in color science. In case anything is wrong, feel free to correct me and I will update this thread accordingly.Technical details:
I will not be very extensive here. In Charles Poynton's ColorFAQ page
there are two documents that explains all the basics about color and color conversion: the ColorFAQ.pdf
and the Colour Space Conversions
It is possible to convert an image captured in a RGB color space with a color gamut defined by a set of primaries, to a color gamut defined by a different set of primaries. For the conversion being without any color loss, the second color gamut should contain entirely the first color gamut (Display with oversaturated primaries - see note 1). In case the second color gamut is contained in the entire first color gamut, you can still use this solution to get accurate colors, but you would never achieve the colors that are out of the gamut (Display with undersaturated primaries - see note 2). The more usual situation is the first.
What we want is converting the colors captured with BT.709, EBU, SMPTE-C or any other source primaries color gamut (the standards used for coding the material) to our display's primaries color gamut.
My first step was writing a little matlab file for calculating these conversion matrices.
In the PC, the RGB data we have is all at the non-linear domain (called R'G'B'), and the conversion between primaries should be done at the linear domain (called RGB). So, for performing the color correction, we have to convert from R'G'B'->RGB, apply the conversion matrix and then convert back from RGB->R'G'B' - our displays need R'G'B'. Since in this process we have the RGB data at the linear domain, we could also correct the gamma accordingly to our displays gamma curve, in case you could not correct it in the display itself.
Solved the problem theoretically, we just need to find a way of doing it...How to do it:
1a-Update your DirectX runtime for the latest version. Go here
1b-Download and install mplayerc with shader support. You can get it here
. I use Casimirs version: MPC Homecinema.
3-Select View->Shader Editor
4-In the top field write the name you want for the shader (I used Color Correction BT.709) and click Enter for adding the new shader entrance.
5-In the field on the right of the name select ps_2_0
6-In the space bellow these two fields, copy and paste the following code:
sampler s0 : register(s0);
float4 p0 : register(c0);
static float4x4 r2r =
\t0.8634, 0.1515, -0.0149, 0,
\t-0.0083, 0.9484, 0.0599, 0,
\t0.02446, 0.0312, 0.9444, 0,
\t0, 0, 0, 0
float4 main(float2 tex : TEXCOORD0) : COLOR
float4 c0 = tex2D(s0, tex);
c0 = pow(c0, 1/0.45);
c0 = mul(r2r, c0);
c0 = saturate(c0);
c0 = pow(c0, 0.45);
It should look like this: Attachment 92193
7-copy your matrix into the code (see "Creating the matrix", bellow)
8-Repeat steps 4 to 7 if you want to color correct other source types, like BT.601 EBU (PAL) or BT-601 SMPTE-C (current NTSC)
9-Go to View->Options->Playback->Output and select:
-DirectShow Video: VMR9(Renderless)
-Surface: 3D Surfaces
It should look like this:Attachment 92192
10-Play your video
11-Go to Play->Shaders and select the shader you want
This method is great because you could pause the image and toggle (Ctrl+P) the shader to see its effect on the image. Also, all the calculations for the correction are done in your graphic cards GPU, not needing more CPU power.Gamma correction:
If you can not adjust your display's gamma curve and it's not the standard value (1/0.45), you can correct it with this method.
The last line of the shader code:
c0 = pow(c0, 0.45);
converts the RGB data again to the R'G'B' using a power function. By default, it uses the standard power function for BT.709.
Changing the power function, you can obtain R'G'B' data that better fits your display and viewing environment.
According to Poynton's gamma faq here
, when you view your display in a dim surround, you should use a power function with a coefficient slightly higher than 1/gamma, he suggests 1.13/gamma. If you view your display in a lit surround, you should use a coefficient toward 1/gamma. You can play a little with this number and see which will please you more. You can even create different shaders for daytime viewing and nightime viewing.
Your display has a gamma of 1.9.
If you use a lit environment for your viewings (daytime), the last line, in your case, should be:
c0 = pow(c0, 1/1.9);
If you use a dim environment for your viewings (evening or light controlled room), the last line, in your case, should be:
Creating the matrix:
c0 = pow(c0, 1.13/1.9);
If you know your display primaries coordinates, you can calculate the matrices needed for color correcting your display.
JohnAd provided this excel file:Attachment 92415
which calculates and outputs the matrix and the shader code ready to use in mplayerc.
You should create a shader for each source you will use. In general, you would want BT.709 and BT.601 EBU or BT.709 and BT.601 SMPTE-C. Not all people see both PAL and NTSC material.
If you don't have a colorimeter to measure your display's primaries coordinates, look at this file:Attachment 92245
I have calculated the matrices for some displays I knew the coordinates.
Note: Even if you have any of the displays indicated in the file, it doesn't mean your will have the same coordinates, but probably it will be close enough...
Here is an example for BT.709 sources with projector JVC RS1/HD1:
0.9347627, 0.0563391, 0.0088982
0.0298623, 0.9461880, 0.0239497
0.0160549, 0.1037023, 0.8802428
Now, let's change the matrix for using it in the shader. Remember we must add a 4th row and 4th column with all zeros.
0.9347627, 0.0563391, 0.0088982, 0,
0.0298623, 0.9461880, 0.0239497, 0,
0.0160549, 0.1037023, 0.8802428, 0,
0, 0, 0, 0
Just copy the corrected matrix in the shader code.
If you want, you could post here your coordinates and I will calculate the matrices for you and update the file above.
As the title says, this could be done to any display you have. If you know your display's primaries coordinates and your source coding standard, you could correct the image to get accurate colors. If you don't know it, you could try to improve your image with some trial and error, but it would be very difficult to get accurate colors.
It would be great if some of you could test this solution and report about it. If you also could post some pics with a before and after would be great!
The fact that I have found this solution does not mean that I think we should correct the colors or that the "wrong" colors bother me. I just think that we should be able to see and decide for ourselves.
Note 1: Since we will have to "shrink" the display's color gamut to have the same size of the source one, there will be less bit steps available, because the full level [0:255] will correspond only to the full display's color gamut. For example: with the shrinking, the full red which is [255,0,0] will be converted to [247,52,40] (BT.709 and RS1 display). This means loosing some color levels and the possibility of appearing a slightly amount of banding, due to the current PC limitation of 8-bit color depth..
Note 2: We will need to "expand" the display's color gamut to have the same size of the source one, but this is not possible, because we cannot go outside of the display's color gamut. We can still have the accurate colors that lie inside the display's color gamut, but for the colors outside it would appear as a severe color "crush". It's not very wise to color correct in this situation...
Note 3: In reality, the conversion matrix is 3x3, but because the shader uses 4x4 matrices, we have to add a 4th column and a 4th row with all zeros, like in the example code. To know how to calculate and select the correct matrices, see next post.
cc_mat.txt 2.0859375k . file
ColourCorrection.zip 9.7255859375k . file