How to draw a L*a*b* colour space or HSV colour space (2D)?

56 visualizzazioni (ultimi 30 giorni)
Hi
I would like to draw the LAB or HSV colour space in 2D (see below) but I don't know how to draw it.
hsl.jpg
I found some code online (see below) which do something very similar (see the graph below) but NOT the same. The main difference is the colour at the center, it should be white, and the colours should graduately getting more and more saturated instead of one same colour from center all the way to the edge. What do I need to do in order to obtain a plot like the first graph??
r = linspace(0,1,10);
theta = linspace(0, 2*pi, 100);
[rg, thg] = meshgrid(r,theta);
[x,y] = pol2cart(thg,rg);
pcolor(x,y,thg);
colormap(hsv);
shading flat;
axis equal;
untitled.jpg
  1 Commento
Guillaume
Guillaume il 6 Mar 2019
Note that HSV and Lab are 3D colour spaces. They can't be fully plotted in 2D. For example, your desired plot is simply a slice of the HSV cylinder at maximum lightness/value. Basically, the top of the HSV cylinder.

Accedi per commentare.

Risposta accettata

Guillaume
Guillaume il 6 Mar 2019
As commented in your question, note that you're only plotting one slice of the 3D colour space.
Of course, your original code doesn't work, you end up doing
pcolor(x, y, thg);
so you're only plotting the hue (angle) against x and y. There's no saturation or value information in your plot. You then apply a colour map called hsv whose colours are derived from the hsv cylinder, but in no way does it mean you've done any conversion to/from hsv. The whole idea is flawed.
Here is how I'd do it:
%inputs:
%plotradius: the plot radius of the disk. Image will be 2*plotradius+1 x 2*plotradius+1
%plotvalue: the Value at which the slice of the HSV cylinder is to be taken. In the range 0-1
plotradius = 100;
plotvalue = 1;
[x, y] = meshgrid(-plotradius:plotradius); %get x,y coordinates of pixels in image
[hue, saturation] = cart2pol(x, y); %convert to angle, radius. Angle is the hue, radius is the saturation in the HSV cylinder
hue = (hue + pi) / (2*pi); %rescale from -pi:pi to 0:1 since matlab use 0:1 for the range of the hue
saturation = saturation / plotradius; %rescale saturation to range 0:1. Not that anything above 1 is outside the cylinder
value = ones(size(hue)) * plotvalue; %set value constant for all points in the disk
%now set points outside the disk (saturation >1) to white. That'd be a value of 1 and saturation of 0. hue doesn't matter
outsidedisk = saturation > 1;
saturation(outsidedisk) = 0;
value(outsidedisk) = 1;
%finally, convert hsv to rgb and plot
rgb = hsv2rgb(cat(3, hue, saturation, value));
imshow(rgb);
  3 Commenti
Guillaume
Guillaume il 6 Mar 2019
Matlab always draw the axis at the border of the image. If you want that:
imshow(rgb, 'XData', [-plotradius, plotradius], 'YData', [-plotradius, plotradius]);
axis on;
You can fake axes in the middle by drawing lines yourselves:
imshow(rgb, 'XData', [-plotradius, plotradius], 'YData', [-plotradius, plotradius]);
ax = gca;
ax.YDir = 'normal';
hold on;
plot(ax.XLim, [0, 0], 'k');
plot([0, 0], ax.YLim, 'k');
plot([ax.XTick; ax.XTick], repmat([-plotradius; plotradius] * ax.TickLength(1) * 3, 1, numel(ax.XTick)), 'k');
plot(repmat([-plotradius; plotradius] * ax.TickLength(1) * 3, 1, numel(ax.YTick)), [ax.YTick; ax.YTick], 'k');
text(ax.XTick, zeros(size(ax.XTick)) + plotradius*ax.TickLength(1)*10, ax.XTickLabel);
text(zeros(size(ax.YTick)) + plotradius * ax.TickLength(1) * 5, ax.YTick, ax.YTickLabel);
hold off;
Marco A. Acevedo Z.
Marco A. Acevedo Z. il 17 Mag 2023
Modificato: Image Analyst il 17 Mag 2023
Thanks Guillaume, it saved me some time :)
I needed the wheel [0-180] clockwise from X-axis positive using value (darkness from 0.5 to 1) at constant saturation. For that, use:
plotradius = 500;
plotsaturation = 1;
[x, y] = meshgrid(-plotradius:plotradius); %get x,y coordinates of pixels in image
%convert to angle, radius. Angle is the hue, radius is the saturation in the HSV cylinder
[hue, value] = cart2pol(x, y); %clock-wise from X-axis
%rescale from -pi:pi to 0:1 since matlab use 0:1 for the range of the hue
hue2 = rescale(hue, 0, 1, 'InputMin', 0, 'InputMax', pi);
%rescale saturation to range 0:1. Not that anything above 1 is outside the cylinder
value = value/plotradius;
%set value constant for all points in the disk
saturation = ones(size(hue)) * plotsaturation;
%now set points outside the disk (saturation >1) to white. That'd be a value of 1 and saturation of 0. hue doesn't matter
outsidedisk = (value > 1) | (hue < 0);
value(outsidedisk) = 0;
saturation(outsidedisk) = 1;
%finally, convert hsv to rgb and plot
rgb = hsv2rgb(cat(3, hue2, saturation, value));
figure
imshow(rgb)
[EDIT] ran the code so we can see the image it makes.

Accedi per commentare.

Più risposte (3)

darova
darova il 6 Mar 2019
clc, clear, cla
m = 100;
n = 10;
r = linspace(0,1,n);
theta = linspace(0, 2*pi, m);
[rg, thg] = meshgrid(r,theta);
[x,y] = pol2cart(thg,rg);
a = hsv(m);
hold on
whitebg('k')
for i = 1:m
for j = 1:n
color = [1 1 1] - (1-a(i,:))*j/n;
plot(x(i,j), y(i,j),'color',color,'marker','.');
end
end
hold off
  1 Commento
Salad Box
Salad Box il 6 Mar 2019
Hi,
Thanks for the answer but I'm not sure whether that's what I asked for. Because I got the below image from your code.
Untitled.png

Accedi per commentare.


Image Analyst
Image Analyst il 6 Mar 2019
See attached demo.
0000 Screenshot.png
0001 Screenshot.png
Capture.PNG
Image.png

DGM
DGM il 10 Mag 2022
One thing that should be pointed out is that when plotting these things in a rectangular image, some consideration should be given to the geometry of what's being shown. The results are otherwise misleading.
When plotting slices of the cylindrical representation of HSV/HSL, it makes sense to omit points outside of the circular S=1 boundary (like @Guillaume's answer does). Points outside that boundary are just truncated.
The same applies for any other color model, but the boundary of interest may not be intuitively obvious beforehand. Consider the extent of sRGB in LAB:
That's not a cylinder or a grid-aligned rectangular prism. Showing it as a set of full rectangular color fields misrepresents its extents. To add confusion, the appearance of out-of-gamut color points is determined by the rendering intent of the conversion tools, which is something that a viewer won't have any way of knowing.
Consider the example:
sz = [300 300]; % size of each slice image
nslices = 25;
% set up base LAB image
A = linspace(-110,110,sz(1));
B = linspace(-110,110,sz(2));
[A B] = meshgrid(A,B);
l = linspace(1,99,nslices);
outpict = cell(nslices,1);
for k = 1:nslices
% construct this slice
thisl = l(k)*ones(sz);
thisrgb = lab2rgb(cat(3,thisl,A,B));
% mark out-of-gamut regions
isoog = any(thisrgb>1,3) | any(thisrgb<0,3);
thisrgb(repmat(isoog,[1 1 3])) = 0;
% flip so that coordinates are correct
thisrgb = flipud(thisrgb);
% add a text label directly to the image
% textim() is part of MIMT (see File Exchange)
lbl = textim(sprintf('L = %d',round(l(k))),'ibm-iso-16x9');
thisrgb(10:size(lbl,1)+9,10:size(lbl,2)+9,:) = repmat(lbl,[1 1 3]);
outpict{k} = thisrgb;
end
% show all slices
montage(outpict)
The above example shows only the in-gamut regions for each slice. Right-click and view the image for more detail.
What if you want to draw a border around the in-gamut regions? In the HSV example, that might seem simple; just overlay a circle. It's not a simple in LAB. See this answer for an example:
Where did that spinning 3D plot come from?
csview('lab','invert')
th = linspace(0,360,25);
outpict = cell(numel(th)-1,1);
for f = 1:numel(th)-1
view(th(f),20)
outpict{f} = iminv(frame2im(getframe(gcf)));
end
outpict = imstacker(outpict,'padding',0);
gifwrite(outpict,'spinlab.gif',0.2);
The functions csview(), iminv(), imstacker(), gifwrite() and textim() are from MIMT on the File Exchange.
Csview() can either be used as a command-line tool like shown, or it can be launched with a GUI for interactive visualization of different color models.

Community Treasure Hunt

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

Start Hunting!

Translated by