convert RGB to YUV and convert YUV to RGB
143 次查看(过去 30 天)
显示 更早的评论
0 个评论
回答(3 个)
DGM
2022-2-28
编辑:DGM
2022-3-1
I just realized that you're probably talking about YCoCg or YCoCg-R.
That should be losslessly invertible (since all factors are powers of 2).
This should be a simple example.
% black, white, all primaries and secondaries
inpict = im2uint8(permute([0 0 0; hsv(6); 1 1 1],[1 3 2]));
% Do a full RGB -> YCoCg -> RGB conversion cycle
A = [0.25 0.5 0.25; 0.5 0 -0.5; -0.25 0.5 -0.25];
Ainv = [1 1 -1; 1 0 1; 1 -1 -1];
wpict = double(inpict); % cast double, but DO NOT rescale
ycc = imapplymatrix(A,wpict); % forward
rgb = imapplymatrix(Ainv,ycc,'uint8'); % reverse
immse(inpict,rgb) % cycle is lossless
Note that doing anything to move any of these values away from powers of 2 will break reversibility. If the source image is converted from [0 255] uint8 to [0 1] scale double, the cycle will not be lossless.
I retract my prior statement that YCoCg should be compatible with offset and padding schemes used by YCbCr conventions. While the chroma range is similar, the retention of fractional values is critical to the reversibility of the cycle.
% show range of each channel of the YCoCg image
% also show the number of non-integer values per channel
for c = 1:3
[min(ycc(:,:,c)) max(ycc(:,:,c)) nnz(mod(ycc(:,:,c),1))]
end
Note that if the YCoCg image is to be stored in integer form, it will require more than 8 bits to do so losslessly. Using YCoCg-R will require fewer extra bits to represent the chroma data, but it will still require more than 8 bits to be lossless. Simply put, the fractional values are all half and quarter-integer, so scaling the transformation accordingly will result in integer-valued YCoCg, but you'd need 2 extra bits to store the result if you scale by 4.
Similarly, trying to rescale the transformation to provide room for the overshoot/undershoot margins used in YCbCr will break reversibility.
TL;DR
YCoCg can do lossless full-cycle conversion of images, but close attention needs to be paid throughout. The YCoCg image will require more bits than the RGB source, so don't just offset chroma by 128 and cast as uint8.
0 个评论
Image Analyst
2022-1-23
编辑:Image Analyst
2022-1-23
This works fine:
% Initialization Steps.
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
clear; % Erase all existing variables. Or clearvars if you want.
workspace; % Make sure the workspace panel is showing.
format long g;
format compact;
fontSize = 14;
rgbImage = imread('peppers.png');
subplot(2, 1, 1);
imshow(rgbImage);
impixelinfo;
YUV = rgb2yuv(rgbImage, 0);
rgbImage2 = yuv2rgb(YUV);
subplot(2, 1, 2);
imshow(rgbImage);
impixelinfo;
theSame = isequal(rgbImage, rgbImage2)
meanSquaredError = immse(double(rgbImage), double(rgbImage2))
p = psnr(double(rgbImage), double(rgbImage2))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%This function rgb2yuv converts the RB matrix of an image to an YUV format%
%matrix for the image. It plots the images, if Plot Flag is eqaul to 1. %
% %
%Example %
%file=('C:\Image0474.jpg'); %
%plotflag=1; %
%RGB = imread(file); %
%imshow(RGB); %
%YUV=rgb2yuv(RGB); %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function YUV=rgb2yuv(RGB,plot_flag)
R = double(RGB(:,:,1));
G = double(RGB(:,:,2));
B = double(RGB(:,:,3));
%Conversion Formula
Y = 0.299 * R + 0.587 * G + 0.114 * B;
U = - 0.168736 * R - 0.331264 * G + 0.5 * B;
V = 0.5 * R - 0.418688 * G - 0.081312 * B;
%Y = 0.299 * R + 0.587 * G + 0.114 * B;
%U = -0.14713 * R - 0.28886 * G + 0.436 * B;
%V = 0.615 * R - 0.51499 * G - 0.10001 * B;
if (plot_flag==1) %plot figures
figure();
subplot(1,3,1);imshow(Y);title('Y');
subplot(1,3,2);imshow(U);title('U');
subplot(1,3,3);imshow(V);title('V');
end
YUV=cat(3,uint8(Y),uint8(U),uint8(V));
YUV=rgb2ycbcr(RGB);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function images=yuv2rgb(YUV)
Y = double(YUV(:,:,1));
U = double(YUV(:,:,2));
V = double(YUV(:,:,3));
% Conversion Formula
R =uint8( 1 * Y + 0 * U + 1.4022* V);
G = uint8(1 * Y -0.3456 * U -0.7145* V);
B= uint8(1 * Y + 1.7710 * U + 0 * V );
image=cat(3,uint8(R),uint8(G),uint8(B));
images=ycbcr2rgb(YUV);
end
The input image is almost identical to the round trip image rgbImage2. "theSame" is equal to false but the biggest difference is 2 gray levels. You cannot get infinite precision when you're taking weighted sums where the weights have 4 decimal places.
theSame =
logical
0
meanSquaredError =
0.388392130533854
p =
52.2380993906641
So that's what I did. What exactly did you do?
3 个评论
DGM
2022-1-23
编辑:DGM
2022-1-23
... you're just using rgb2ycbcr() and ycbcr2rgb() to create the outputs. All of the other conversion code is unused.
Granted, YCbCr is probably what everyone means when they say YUV, but if the other conversions are supposed to be used, then they need to include the offsets to avoid data truncation and the appropriate margins specified for the integer width.
EDIT: I only now realized that this code was directly from the FEX submission. Oof. My bad.
To clarify, the functions from this submission
are nothing more than wrappers for rgb2ycbcr() and ycbcr2rgb() with a bunch of extraneous and incorrect math that gets discarded. Just use rgb2ycbcr() and ycbcr2rgb().
DGM
2022-1-23
编辑:DGM
2022-1-24
@mohammad nemat You're not going to get lossless conversion from RGB to YCbCr and back. The inputs and outputs are integer-valued. Nothing about the linear transformations guarantees that integers map directly to integers. There will be rounding.
inpict = imread('peppers.png');
yccpict = rgb2ycbcr(inpict);
rgbpict = ycbcr2rgb(yccpict);
immse(inpict,rgbpict)
Note that these YCbCr conversion tools will produce unit-scale floating-point output if fed a floating-point image. While this avoids integer rounding error, there is still error in floating-point math. It's also worth noting that I don't know that such unit-scale representations of YCbCr are commonplace, so it may be largely incompatible with other things anyway.
inpict = im2double(inpict);
yccpict = rgb2ycbcr(inpict);
rgbpict = ycbcr2rgb(yccpict);
immse(inpict,rgbpict)
Regarding error, it's worth noting that if you're pursuing chroma subsampling like the second FEX tool you linked, the RGB-YCbCr-RGB conversion path will necessarily be significantly lossy, as you're discarding half of the color information by subsampling.
EDIT:
For sake of reference, this is how the conversion is done for 8-bit YCbCr, though in practice there are probably more convenient methods. This is simply intended to show that everything here is simple linear transformations (scaling, shifting). The error is no worse than with the existing tools.
inpict = imread('peppers.png');
% transformation matrix
A = [0.299 0.587 0.114;-0.1687 -0.3313 0.5;0.5 -0.4187 -0.08131];
Asc = [219; 224; 224]; % scaling for uint8
os = [16; 128; 128]; % offset
% convert to 8-bit YCbCr
s = size(inpict);
yccpict = uint8(reshape(reshape(im2double(inpict),[],3)*(A.*Asc).' + os.',s));
yccpict2 = rgb2ycbcr(inpict);
immse(yccpict,yccpict2)
% convert to 8-bit RGB
rgbpict = im2uint8(reshape((reshape(double(yccpict),[],3) - os.')/(A.*Asc).',s));
rgbpict2 = ycbcr2rgb(yccpict2);
immse(rgbpict,inpict)
immse(rgbpict2,inpict)
imshow(rgbpict)
mohammad nemat
2022-1-24
编辑:mohammad nemat
2022-1-24
3 个评论
DGM
2022-1-25
编辑:DGM
2022-1-25
Let me clarify. For some reason, everyone likes to call all luma-chroma models "YUV". They are not all the same.
Both of the functions you're using from the File Exchange -- both tools called rgb2yuv() convert to YCbCr, not YUV. If you're dealing with any sort of digital video format which stores data in unsigned integer format, it's almost certainly YCbCr. The raison d'etre for YCbCr is the transport of digital video. YUV is a much older luma chroma transformation used for analog video. Unless you're dealing with analog PAL video, you're probably not using YUV.
The easy way to tell is to just look at the forward transformation matrix:
% YCbCr/YPbPr with Rec 470/601 luma
A = [0.299 0.587 0.114;-0.1687 -0.3313 0.5;0.5 -0.4187 -0.08131];
% YUV with Rec 470/601 luma
A = [0.299 0.587 0.114; -0.14713 -0.28886 0.436; 0.615 -0.51499 -0.10001];
In the first case, the nominal range of the chroma components are both 2*0.5=1. They're equal and normalized and so they easily scale to fit integer data range. In the case of YUV, the chroma components have different widths (2*0.436=0.872 and 2*0.615=1.23). If uniformly scaled to an integer data range, either U would make poor use of the range of an integer class, or V would get truncated. These scaling factors are the same regardless of which luma constants are used.
As @Image Analyst points out, The projections of sRGB into YUV and YCbCr are similar. The only differences are the axis scaling and offsets and the expected numeric type. In terms of separating the components of brightness and color, they're basically the same.
With all that said, I'm still assuming that you want YCbCr conversion, as that's what you were showing. If you want actual YUV conversion, you can use imapplymatrix or other methods:
inpict = imread('peppers.png');
% YUV with Rec 470/601 luma
A = [0.299 0.587 0.114; -0.14713 -0.28886 0.436; 0.615 -0.51499 -0.10001];
inpict = im2double(inpict);
lumachromapict = imapplymatrix(A,inpict);
rgbpict = imapplymatrix(inv(A),lumachromapict);
immse(inpict,rgbpict)
... but since these are all done in floating point, error must be expected.
If you have a lossless conversion method (for YCbCr) that can map integers directly to integers, feel free to share it. Even if you have such a thing, why would you expect zero error if you edited the image before converting back to RGB?
For an example, consider that it's trivial (if inelegant) to effect such a perfect integer-to-integer conversion. Simply round the values until they map cycliclally.
inpict = imread('peppers.png');
% weed out all the values so they map perfectly
yccpict = rgb2ycbcr(inpict);
inpict = ycbcr2rgb(yccpict);
yccpict = rgb2ycbcr(inpict);
inpict = ycbcr2rgb(yccpict);
% now it converts perfectly
yccpict = rgb2ycbcr(inpict);
rgbpict = ycbcr2rgb(yccpict);
immse(inpict,rgbpict) % zero error!
Of course if you edit the image before converting back to RGB, it won't be perfect, even if the conversion cycle is (as demonstrated) "lossless".
inpict = imread('peppers.png');
% weed out all the values so they map perfectly
yccpict = rgb2ycbcr(inpict);
inpict = ycbcr2rgb(yccpict);
yccpict = rgb2ycbcr(inpict);
inpict = ycbcr2rgb(yccpict);
% now it converts perfectly
yccpict = rgb2ycbcr(inpict);
yccpict(1) = yccpict(1)+1; % the smallest possible edit
rgbpict = ycbcr2rgb(yccpict);
immse(inpict,rgbpict)
另请参阅
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!