Fix out-of-gamut RGB colors

8 views (last 30 days)
Roger Breton
Roger Breton on 31 Mar 2022
Edited: DGM on 1 Apr 2022
I'm going in circle... I have this code, which converts from Lab to RGB :
rawRGB = lab2rgb(Lab,'WhitePoint','d50')
Trouble is, there are loads of RGB colors that are out of gamut and come out with either negative values or values greater than one.
So I use this code to hunt down for out-of-gamut values :
isoog_1 = any(rawRGB>1 ,3);
isoog_0 = any(rawRGB<0 ,3);
But I'm lost when it comes time to use this information to substitue out-of-gamut colors in my rawRGB matrix for 0s and 1s. I tried this :
rawRGB(repmat(isoog_1,[1 1 3])) = 1;
rawRGB(:,4:9) = []
rawRGB(repmat(isoog_0,[1 1 3])) = 0;
rawRGB(:,4:9) = []
But I must be doing something 'illegal' which I can't figure out, because I get this error :
Attempt to grow array along ambiguous dimension.
This logic worked perfectly fine on this code though (I extracted row 1062 from the Lab matrix to experiment with) :
Test = Lab(1062,:) % 7.5PB 4/26
% Test = 38.57 54.97 -100.01
TestRGB = lab2rgb(Test,'WhitePoint','d50')
% TestRGB = 0.2537 0.2216 1.0216
isoog_1 = any(TestRGB>1 ,3);
isoog_0 = any(TestRGB<0 ,3);
TestRGB(repmat(isoog_1,[1 1 3])) = 1;
TestRGB(:,4:9) = []
TestRGB2 = [0.34 -0.34 0.98]
isoog_0 = any(TestRGB2<0 ,3);
TestRGB2(repmat(isoog_0,[1 1 3])) = 0;
I guess I don't see how to go about conditionnaly substituting values inside a matrix...
Any help is appreciated.

Accepted Answer

Stephen23
Stephen23 on 31 Mar 2022
Edited: Stephen23 on 31 Mar 2022
Simpler and much more efficient:
rawRGB = max(0,min(1,rawRGB));

More Answers (1)

Roger Breton
Roger Breton on 31 Mar 2022
I think I found a solution, alhough it is not elegant... :
rawRGB = lab2rgb(Lab,'WhitePoint','d50')
TempR = rawRGB(:,1)
TempR(TempR>1)=1
TempR(TempR<0)=0
TempG = rawRGB(:,2)
TempG(TempG>1)=1
TempG(TempG<0)=0
TempB = rawRGB(:,3)
TempB(TempB>1)=1
TempB(TempB<0)=0
rawTemp = [TempR TempG TempB ]
It beats looping over 2700 elements to find negative and greater than 1 values....
  3 Comments
Roger Breton
Roger Breton on 31 Mar 2022
John, I've embarked on a journey to study Munsell 'Real' colors.
I have a copy of the glossy edition of the Munsell Book of colors, I picked up on eBay a few years ago, from which I created a custom color library (*.ACB) for use in Photoshop, based on spectral measurements I made of the whole 1324 chips in the book (took a whole day), one chip at a time, using my GretagMacbeth SpectroEye. Based on this library, students can analyze any RGB or CMYK image, in Photoshop, by merely clicking away on some pixels, using the Color picker tool, and have Photoshop come up with the closest matching Munsell color notation :
It works great, except, the Book 'gamut' is quite limited? (Because of real pigments limitations, lightfastedness, etc...). That's why I am experimenting with the 1943 Renotation data, which can be found in Color Science by Wisjecki & Stiles, and at RIT Munsell Institute of Color Science (Farichild, Burns et al) and elsewhere on the net. I'm not 100% clear on the data, yet, but I have converted the xyY to Lab D50, through a Bradford chromatic adaptation (from illuminant C to D50), and, using good old Matlab, I'm able to get a scatter3 kind of plot of what those "Renotation colors" look like :
Here is my scatter3 statement:
scatter3(Lab(:,2),Lab(:,3),Lab(:,1),150,rawRGB,'fill', 'MarkerEdgeColor', [0.5,0.5,0.5], 'Linewidth', 0.5);
I'm getting the rawRGB data for the colormap through this conversion :
rawRGB = lab2rgb(Lab,'WhitePoint','d50')
Far from ideal but it's start. As you indicated, the colors are smashed to the sRGB gamut but at least, I get some visualization of the Renotation data. Thanks so very much for your kind guidance!
DGM
DGM on 1 Apr 2022
Edited: DGM on 1 Apr 2022
I mentioned MIMT maxchroma() earlier. That would allow you to find the gamut extents in LCHab and then simply clamp chroma prior to conversion. It might not be the universally best approach, but it at least keeps colors from migrating to the corners of the RGB cube during truncation. In practice, you wouldn't need to use it directly, as MIMT lch2rgb() uses it internally when the 'truncatelch' option is specified.
EDIT: wait. If you're using d50, then maxchroma won't work. I never bothered to set it up for d50, but there's no reason that the same approach couldn't be used.

Sign in to comment.

Products


Release

R2021a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!