# How to convert rgb to YCbCr?

I looked at ColorConvert, but haven't found a relevant option. Is there a built-in function do this?

This is using the digital formula given at Wikipedia. ImageAdjust was then used to scale computed YCbCR levels from 16..235 back to 0..1 for final display.

ClearAll[rgb2YCbCr]
rgb2YCbCr[{r_, g_, b_}] :=(*ref:http://en.wikipedia.org/wiki/YCbCr, digital formula*)
{16 + (65.481 r + 128.553 g + 24.966 b),
128 + (-37.797 r - 74.203 g + 112 b),
128 + (112 r - 93.786 g - 18.214 b)
};

lena = Import["ExampleData/lena.tif"];
lenaYCbCr = Map[rgb2YCbCr, ImageData[lena], {2}];
{red, blue, green} = MapThread[ImageMultiply, {ColorSeparate[lena],
{Red, Green,Blue}}];    (*thanks to chat room for this trick*)

c1 = lenaYCbCr; c1[[All, All, {2, 3}]] = 1;
c2 = lenaYCbCr; c2[[All, All, {1, 3}]] = 1;
c3 = lenaYCbCr; c3[[All, All, {1, 2}]] = 1;

n = 300;
Grid[{
{"RGB", "YCbCr", SpanFromLeft}, {Image[lena, ImageSize -> 350],
{Image[red, ImageSize -> n], "channel 1", ImageAdjust@Image[c1, ImageSize -> n]},
{Image[green, ImageSize -> n], "channel 2", ImageAdjust@Image[c2, ImageSize -> n]},
{Image[blue, ImageSize -> n], "channel 3", ImageAdjust@Image[c3, ImageSize -> n]}
}, Alignment -> Center, Frame -> True,
FrameStyle -> Directive[Thickness[.5], LightGray]
]


This below shows the 3 channels of each image (for faster implementation of rgb2YCbCr[] please see MrWizard's code on this page)

• Just as a side note, lena2=Map[rgb2YCbCr, ImageData[lena], {2}] should suffice instead of all those temporary variables. Aug 4 '13 at 9:35

Despite all the efforts of Nasser and MrWizard to make this fast, I believe the most natural way to do this is to use ImageApply because it is fast, it rescales the data for you and you don't have to extract the ImageData by yourself. So taking the transformation matrix directly from Wikipedia for normalised RGB values and using their sample image too you get

img = Import[
m = {{0.299, .587, .114}, {-.168736, -.331264, .5}, {.5, -.418688, -.081312}};


the conversion is simply

ImageApply[m.# &, img]


This is almost twice as fast as the solution of MrWizard on my machine here (0.640s compared to 0.356s), but note that this was only tested on Mathematica 8 and 9 on Linux and MacOSX.

Furthermore, be aware that the two colour channels are in the interval [-0.5, 0.5] so using ColorSeparate will produce very dark images in the last two channels.

If you want to create the visualisation of the different channels, which is shown on the right side on the Wikipedia article page, you could simply use the inverse matrix. I shifted the colour channels by 0.5 here for display purposes

mi = Inverse[m];

ImageApply[#1, #2] & @@@
Transpose[{{mi.{#, 0, 0} &, mi.{.5, # - .5, 0} &, mi.{.5, 0, # - .5} &},
ColorSeparate[ImageApply[m.# + {0, .5, .5} &, img]]}] ## Timings

Regarding Mr.Wizards comment

This is five times slower than my method on my system (v7). Before I reconsider my vote can you explain why this would be faster than Dot in later versions?

I compared the following timings,

ImageApply[m.#+{0.0,0.5,0.5}&, img] // Timing // First
RGBtoYCbCr[img] // Timing // First


where first line is the method I described with the shift of {0,1/2,1/2} included to make it more comparable to MrWizards method. The second line is the function from Mr.Wizards code.

Running this code 100 times to mean out cosmic radiation, I get the following mean timings

• Linux, Mathematica 9.0.1 halirutan: 0.305819 Mr.Wizard: 0.381984
• Linux, M8.0.4 halirutan: halirutan: 0.354542 Mr.Wizard: 0.394065

I believe the difference in timing is purely the overhead of pattern matching and ImageData call.

• That vote-unvote was mine. This is five times slower than my method on my system (v7). Before I reconsider my vote can you explain why this would be faster than Dot in later versions? Aug 5 '13 at 4:47
• I couldn't figure out how to recreate that visualization yesterday so this made me glad. :) (btw mi = Inverse[m]; is omitted). +1 Aug 5 '13 at 7:36
• @Anon Thanks, I fixed it. I haven't tried the code of my answer yesterday. Usually I do this and check whether everything works on a fresh kernel. Aug 5 '13 at 7:38
• @Mr.Wizard I think I don't understand you last sentence in your comment. I use Dot too and I think the different timing has nothing to do with it. The real purpose of my answer was to show that you can do the conversion directly on the image with ImageApply and you are still very fast. (Btw, I have no version 7 installed where I could test it) Aug 5 '13 at 7:41
• Pardon, I meant using Dot on the entire array at once versus using ImageApply to (seemingly) apply Dot to each triplet individually. I wonder if there is some special compilation taking place in v8+ for ImageApply. As I said it's slower in v7. By the way is m.# &+{0.0,0.5,0.5} merely nonfunctional notation or is that also doing something special? Aug 5 '13 at 8:27

Here is an array-optimized version of Nasser's formula. It is up to two orders of magnitude faster.

It handles data with an array depth of 1, 2, or 3, or an Image object.

Timing comparision:

horse = Import["http://i.stack.imgur.com/ZhYwB.jpg"];

Map[rgb2YCbCr, ImageData[horse], {2}] // Timing // First

RGBtoYCbCr[horse]                     // Timing // First


6.303

0.081

Sample output:

ImageAdjust @ Image[RGBtoYCbCr @ horse, ImageSize -> 500] ### Code:

With[{
v1 = DeveloperToPackedArray @ {16, 128, 128},
m1 = DeveloperToPackedArray @
{{65.481, 128.553, 24.966}, {-37.797, -74.203, 112}, {112, -93.786, -18.214}}
},
RGBtoYCbCr[a : {_, _, _}?VectorQ] := v1 + m1.a;
RGBtoYCbCr[img_Image] := RGBtoYCbCr @ ImageData[img, "Byte"];
RGBtoYCbCr[a_ /; MatchQ[Dimensions@a, {_, 3}]] := Transpose[v1 + m1.Transpose[a]];
RGBtoYCbCr[a_ /; MatchQ[Dimensions@a, {_, _, 3}]] :=
Transpose[v1 + m1.Transpose[a, {2, 3, 1}], {3, 1, 2}];
]


A compiled version of Nasser's formula,

ClearAll[cRGB2YCbCr]
cRGB2YCbCr= Compile[{{rgb, _Real, 1}},
{16 + (65.481 r + 128.553 g + 24.966 b),
128 + (-37.797 r - 74.203 g + 112 b),
128 + (112 r - 93.786 g - 18.214 b)} /.
Quiet@Thread[{r, g, b} -> Array[rgb[[#]] &, 3]] // Evaluate,
RuntimeAttributes -> Listable
];

horse = Import["http://i.stack.imgur.com/ZhYwB.jpg"];
cRGB2YCbCr@ImageData[horse];//AbsoluteTiming
`