Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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

share|improve this question
add comment

4 Answers

up vote 6 down vote accepted

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], 
   ImageAdjust[Image[lenaYCbCr, ImageSize -> 350]], SpanFromLeft},
  {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

Mathematica graphics

(for faster implementation of rgb2YCbCr[] please see MrWizard's code on this page)

share|improve this answer
    
Just as a side note, lena2=Map[rgb2YCbCr, ImageData[lena], {2}] should suffice instead of all those temporary variables. –  Pickett Aug 4 '13 at 9:35
add comment

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]

enter image description here


Code:

With[{
  v1 = Developer`ToPackedArray @ {16`, 128`, 128`},
  m1 = Developer`ToPackedArray @ 
    {{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}];
]
share|improve this answer
add comment

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[
  "http://upload.wikimedia.org/wikipedia/commons/d/d0/Barns_grand_tetons.jpg"];
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]]}]

Mathematica graphics

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.

share|improve this answer
    
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? –  Mr.Wizard 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 –  Pickett 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. –  halirutan 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) –  halirutan 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? –  Mr.Wizard Aug 5 '13 at 8:27
show 2 more comments

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
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.