Mercurial > forge
changeset 11687:2c39ee206f66 octave-forge
rgb2ycbcr: complete rewrite
* calculations are now made with double precision with a color matrix
* can now accept images of class double, single, uint8 and uint16
* can now accept colormaps
* added tests
* new argument to specify standard
* new argument to specify Kb and Kr
author | carandraug |
---|---|
date | Sun, 05 May 2013 07:48:19 +0000 |
parents | 9f74cc14b4e6 |
children | 101a3bd532ed |
files | main/image/NEWS main/image/inst/rgb2ycbcr.m |
diffstat | 2 files changed, 116 insertions(+), 20 deletions(-) [+] |
line wrap: on
line diff
--- a/main/image/NEWS Sun May 05 04:09:40 2013 +0000 +++ b/main/image/NEWS Sun May 05 07:48:19 2013 +0000 @@ -30,6 +30,10 @@ imrotate_Fourier + ** rgb2ycbcr was completely rewritten to accept images of other classes, and + colormaps. A new argument was implemented to convert the RGB values + according to different standards. + ** The use of non logical matrices to specify the neighborhood for the medfilt2 function has been deprecated. If using a vector to specify the size of the neighborhood, the elements are no longer swapped.
--- a/main/image/inst/rgb2ycbcr.m Sun May 05 04:09:40 2013 +0000 +++ b/main/image/inst/rgb2ycbcr.m Sun May 05 07:48:19 2013 +0000 @@ -1,4 +1,4 @@ -## Copyright (C) 2011 Santiago Reyes González <mailalcuete@gmail.com> +## Copyright (C) 2013 Carnë Draug <carandraug@octave.org> ## ## This program is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free Software @@ -14,29 +14,121 @@ ## this program; if not, see <http://www.gnu.org/licenses/>. ## -*- texinfo -*- -## @deftypefn {Function File} @var{B} = rgb2ycbcr (@var{A}) -## Perform colour convertion from RGB to YCbCr on a given image. +## @deftypefn {Function File} {@var{YCbCrmap} =} rgb2ycbcr (@var{cmap}) +## @deftypefnx {Function File} {@var{YCbCr} =} rgb2ycbcr (@var{RGB}) +## @deftypefnx {Function File} {@dots{} =} rgb2ycbcr (@dots{}, [@var{Kb} @var{Kr}]) +## @deftypefnx {Function File} {@dots{} =} rgb2ycbcr (@dots{}, @var{standard}) +## Convert RGB values to YCbCr. +## +## The convertion changes the image @var{RGB} or colormap @var{cmap}, from +## the RGB color model to YCbCr (luminance, chrominance blue, and chrominance +## red). @var{RGB} must be of class double, single, uint8, or uint16. ## -## The image @var{A} must be a NxMx3 image. The conversion -## The convertion changes the image from the RGB color model to YCbCr e.g. -## @example -## imOut = rgb2ycbcr(imIn); -## @end example -## Currently this function only works with @samp{uint8} and will always return -## an @samp{uint8} matrix. -## @seealso{cmunique} +## The formula used for the conversion is dependent on two constants, @var{Kb} +## and @var{Kr} which can be specified individually, or according to existing +## standards: +## +## @table @asis +## @item "601" (default) +## According to the ITU-R BT.601 (formerly CCIR 601) standard. Its values +## of @var{Kb} and @var{Kr} are 0.114 and 0.299 respectively. +## @item "709" (default) +## According to the ITU-R BT.709 standard. Its values of @var{Kb} and +## @var{Kr} are 0.0722 and 0.2116 respectively. +## @end table +## +## @seealso{hsv2rgb, ntsc2rgb, rgb2hsv, rgb2ntsc} ## @end deftypefn -function im_out = rgb2ycbcr(im) - if (nargin != 1) - print_usage; - elseif (length(size(im)) != 3 || size(im,3) != 3) - error("image must be NxMx3"); +function ycbcr = rgb2ycbcr (rgb, standard = "601") + + img = false; # was input an image? + + if (nargin < 1 || nargin > 2) + print_usage (); + endif + + if (iscolormap (rgb)) + ## do nothing, it's a colormap + elseif (isrgb (rgb)) + img = true; + ## if we have it in 2D, we can use matrix multiplcation + nRows = rows (rgb); + nCols = columns (rgb); + rgb = reshape (rgb, [nRows*nCols 3]); + else + error ("rgb2ycbcr: input must be a colormap (Nx3) or RGB image (NxMx3)"); + endif + + ## TODO would be interesting to accept arbitrary values of Kb and Kr + if (! ischar (standard)) + error ("rgb2ycbcr; STANDARD must be a string `601' or `709'"); + elseif (strcmpi (standard, "601")) + ## these are the values for ITU-R BT.601 + Kb = 0.114; + Kr = 0.299; + elseif (strcmpi (standard, "709")) + ## these are the values for ITU-R BT.709 + Kb = 0.0722; + Kr = 0.2126; + else + error ("rgb2ycbcr: unknown standard `%s'", standard); endif - im = im2double(im); - im_out(:,:,1) = uint8(floor(77*im(:,:,1) + 150*im(:,:,2) + 29*im(:,:,3))); - im_out(:,:,2) = uint8(floor(((-44*im(:,:,1) - 87*im(:,:,2) + 131*im(:,:,3))/256 + 0.5)*256)); - im_out(:,:,3) = uint8(floor(((131*im(:,:,1) - 110*im(:,:,2) - 21*im(:,:,3))/256 + 0.5)*256)); + ## the color matrix for the conversion. Derived from: + ## Y = Kr*R + (1-Kr-Kb)*G + kb*B + ## Cb = (1/2) * ((B-Y)/(1-Kb)) + ## Cr = (1/2) * ((R-Y)/(1-Kr)) + ## It expects RGB values in the range [0 1], and returns Y in the + ## range [0 1], and Cb and Cr in the range [-0.5 0.5] + cmat = [ Kr (1-Kr-Kb) Kb + -(Kr/(2-2*Kb)) -(1-Kr-Kb)/(2-2*Kb) 0.5 + 0.5 -(1-Kr-Kb)/(2-2*Kr) -(Kb/(2-2*Kr)) ]; + + cls = class (rgb); + rgb = im2double (rgb); + ycbcr = (cmat * rgb')'; # transpose in the end to get back colormap shape + ## rescale Cb and Cr to range [0 1] + ycbcr(:, [2 3]) += 0.5; + ## footroom and headroom will take from the range 16/255 each for Cb and Cr, + ## and 16/255 and 20/255 for Y. So we have to compress the values of the + ## space, and then shift forward + ycbcr(:,1) = (ycbcr(:,1) * 219/255) + 16/255; + ycbcr(:,[2 3]) = (ycbcr(:,[2 3]) * 223/255) + 16/255; + switch (cls) + case {"single", "double"} + ## do nothing. All is good + case "uint8" + ycbcr = im2uint8 (ycbcr); + case "uint16" + ycbcr = im2uint16 (ycbcr); + otherwise + error ("rgb2ycbcr: unsupported image class %s", cls); + endswitch + + if (img) + ## put the image back together + ycbcr = reshape (ycbcr, [nRows nCols 3]); + endif endfunction + +%!test +%! in(:,:,1) = magic (5); +%! in(:,:,2) = magic (5); +%! in(:,:,3) = magic (5); +%! out(:,:,1) = [31 37 17 23 29 +%! 36 20 22 28 30 +%! 19 21 27 33 35 +%! 25 26 32 34 19 +%! 25 31 37 18 24]; +%! out(:,:,2) = 128; +%! out(:,:,3) = 128; +%! assert (rgb2ycbcr (uint8 (in)), uint8 (out)); + +%!test +%! out(1:10, 1) = linspace (16/255, 235/255, 10); +%! out(:, [2 3]) = 0.5; +%! assert (rgb2ycbcr (gray (10)), out, 0.00001); + +%!assert (rgb2ycbcr ([1 1 1]), [0.92157 0.5 0.5], 0.0001);