view main/image/inst/rgb2ycbcr.m @ 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 f37de1ef1332
children 101a3bd532ed
line wrap: on
line source

## 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
## Foundation; either version 3 of the License, or (at your option) any later
## version.
##
## This program is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
## details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, see <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @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 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 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

  ## 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);