How can i plot the YCbCr histograms with the correct colour points (0-255)

13 Ansichten (letzte 30 Tage)
Hi,
So i am trying to replicate one of MATLAB's plots but unsure how to get the varying change in colour in the histograms that depend on the pixel value (0-255). YCbCr file is what im trying to achieve. This is what i have so far.
img = imread('peppers.png');
ycbcr=rgb2ycbcr(img);
Y = ycbcr(:,:,1);
Cb = ycbcr(:,:,2);
Cr = ycbcr(:,:,3);
figure;
subplot(311);
histogram(Y,'BinMethod','integers','EdgeAlpha',0,'FaceAlpha',1);
title('Y');xlim([0 255]);
subplot(312);
histogram(Cb,'BinMethod','integers','EdgeAlpha',0,'FaceAlpha',0.7);
title('Cb');xlim([0 255]);
subplot(313);
histogram(Cr,'BinMethod','integers','EdgeAlpha',0,'FaceAlpha',0.7);
title('Cr');xlim([0 255]);

Akzeptierte Antwort

DGM
DGM am 27 Mär. 2023
Bearbeitet: DGM am 18 Mai 2023
Personally, I've always thought those histograms were a bad idea. The fact that the bins are colored against a background of similar lightness makes them very difficult to read in any detail. In short, it would be a more complicated task to create something that I argue is less useful.
Method 1
Using a colorstrip under a regular histogram is what imhist() does. This can be extended to represent Cb/Cr in pseudocolor as follows:
% an image in YCbCr
inpict = imread('peppers.png');
yccpict = rgb2ycbcr(inpict);
% some things we'll need first
x0 = [54 133];
CT0cb = [0.986 1 0.367; 0.604 1 0.985];
CT0cr = [0.261 1 0.139; 0.753 0.706 0.758];
% generate the colormaps for each channel
CTycc = cell(3,1);
CTycc{1} = gray(220); % luma
CTycc{2} = imclamp(interp1(x0,CT0cb,linspace(0,255,225),'linear','extrap')); % Cb
CTycc{3} = imclamp(interp1(x0,CT0cr,linspace(0,255,225),'linear','extrap')); % Cr
% generate the histograms
for c = 1:3
subplot(3,1,c)
imhist(yccpict(:,:,c));
% build padded colorstripe
if c == 1
% use gray padding for the luma bar
% that way neither end disappears into the padding
thisCS = [0.7*ones(16,3); CTycc{c}; 0.7*ones(20,3)];
else
thisCS = [zeros(16,3); CTycc{c}; zeros(15,3)];
end
% update the colorstripe
hi = findobj('type','image');
hi(1).CData = permute(thisCS,[3 1 2]);
end
Where did those values come from in CT0cb and CT0cr? Those approximately define the colormap used by the histogram tools used by the Color Thresholder app. It's worth noting that those are all piecewise-linear in RGB, and they aren't actually axis-aligned in YCbCr. It's just a pretty color sweep. It's not technically accurate or anything.
Note that because I used a colorstrip instead of trying to color the histogram bars, the full sweep is visible, regardless of the color distribution in the image. Also note that I added padding bars to indicate the margin regions where image data shouldn't normally exist.
Method 2
Alternatively, if you really really want to to color the histogram, then getting each bin colored is the challenge. Unlike scatter(), stem objects (what imhist() uses) don't support independent coloring of the stems. A bar chart might be one option, but with the number of bins you'd be using, the setup might have some limitations. I don't know what the Color Thresholder App uses, but here's one way.
% an image in YCbCr
inpict = imread('peppers.png');
yccpict = rgb2ycbcr(inpict);
% some things we'll need first
x0 = [54 133];
CT0cb = [0.986 1 0.367; 0.604 1 0.985];
CT0cr = [0.261 1 0.139; 0.753 0.706 0.758];
CTycc = cell(3,1);
CTycc{1} = gray(220); % luma
CTycc{2} = imclamp(interp1(x0,CT0cb,linspace(0,255,225),'linear','extrap')); % Cb
CTycc{3} = imclamp(interp1(x0,CT0cr,linspace(0,255,225),'linear','extrap')); % Cr
% generate the histograms
for c = 1:3
subplot(3,1,c)
% build padded colormaps
if c == 1
thisCS = [zeros(16,3); CTycc{c}; zeros(20,3)];
else
thisCS = [zeros(16,3); CTycc{c}; zeros(15,3)];
end
% construct patch object
ytop = imhist(yccpict(:,:,c)).';
ybot = zeros(size(ytop));
xrange = getrangefromclass(yccpict(:,:,c));
x = linspace(xrange(1),xrange(2),numel(ytop));
xc = linspace(0,1,numel(ytop));
hp = patch([x fliplr(x)],[ybot fliplr(ytop)],[xc fliplr(xc)]);
hp.EdgeColor = 'none';
hold on; grid on
% plot envelope curve to make sure it's even visible
plot(x,ytop,'k','linewidth',1);
% set the colormap
colormap(gca,CTycc{c})
% set y-scaling to mimic imhist()
ylim([0 2.5*sqrt(ytop*ytop.'/length(ytop))])
xlim(xrange)
end
Note that I added a line which traces out the envelope of the histogram. Without it, the top end of the Y histogram would be completely invisible.
  2 Kommentare
george masters
george masters am 27 Mär. 2023
Hi,
i agree with your first answer, i think it actually looks better and displays more detail using the colour strips at the bottom! It works great actually!
Thank you!
DGM
DGM am 27 Mär. 2023
Bearbeitet: DGM am 18 Mai 2023
Notes on the colormaps
As I said, the color sweeps that are used to make the original histograms are just piecewise linear RGB sweeps -- piecewise only because the simple linear trajectory gets truncated as it reaches the faces of the RGB cube. If we were to plot these in YCbCr, they would look like this
Note that they are not aligned even remotely with the Cb,Cr axes, nor are they mutually-orthogonal. I have no idea why these were chosen for this purpose. The fact that there is so little isolation of the color components makes these maps misleading.
It might make more sense to use something grid-aligned in the represented color space. For example:
% an image in YCbCr
inpict = imread('peppers.png');
yccpict = rgb2ycbcr(inpict);
% generate the colormaps for each channel
CTycc = cell(3,1);
CTycc{1} = gray(220); % luma
ramp = (16:240).'; % full chroma swing
mid = 128*ones(size(ramp)); % both Cb,Cr trajectories cross the neutral axis
Y = 0.7*255*ones(size(ramp)); % luma at which Cb,Cr are sampled
CTycc{2} = ycbcr2rgb(uint8([Y ramp mid])); % Cb
CTycc{3} = ycbcr2rgb(uint8([Y mid ramp])); % Cr
% generate the histograms
for c = 1:3
subplot(3,1,c)
imhist(yccpict(:,:,c));
% build padded colorstripe
if c == 1
% use gray padding for the luma bar
% that way neither end disappears into the padding
thisCS = [0.7*ones(16,3); CTycc{c}; 0.7*ones(20,3)];
else
thisCS = [zeros(16,3); CTycc{c}; zeros(15,3)];
end
% update the colorstripe
hi = findobj('type','image');
hi(1).CData = permute(thisCS,[3 1 2]);
end
Those colors aren't as vibrant and pretty, but they actually make some sense in YCbCr.
Again, the trajectory is piecewise-linear due to the fact that there's no way a constant-luma full chroma swing will stay in gamut.
Note that these trajectories are largely grid-aligned and orthogonal.
While I plotted this in YPbPr out of my own convenience, YCbCr and YPbPr differ only in scaling and offsets. These plots would look identical in YCbCr. The only difference would be the axis ticks and labels.

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (1)

David Szwer
David Szwer am 27 Mär. 2023
You would want to change the CData property of the histograms, which would let you set the colour of each individual bar. However, I don't think histograms actually have this property - only bar charts:
You probably need to get the histogram counts, and then manually plot a graph of them. Someone called Wolfie on StackExchange created this example. I'll copy it here for reference (changing the data variable x to a random array, just so it is runnable), but they deserve the credit!
[h,edges] = histcounts(rand([1 100]),10); % calculate the histogram data
b = bar( (edges(1:end-1)+edges(2:end))/2, h ); % plot the bar chart
b.BarWidth = 1; % make the bars full width to look the same as 'histogram'
b.CData = parula( 10 ); % generate colours as a 10x3 array (columns are RGB), can
% do this manually if you want
b.FaceColor = 'flat'; % Make 'bar' use the CData colours

Community Treasure Hunt

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

Start Hunting!

Translated by