Slow image processing using ReplaceAll

Can anyone explain me, why this line of simple code is so slow? And can it be improved somehow?

p = Import["http://www.ultimatte-software.com/images/2FSN-TLW-wide03-ultimatte-software-blue.jpg"];

Timing[Image[(ImageData[p] /. {r_, g_, b_} -> {b} 2 - {g} - {r})]]

• You could try ImageApply[2 #[[3]] - #[[2]] - #[[1]] & , p] but it's slightly different... – cormullion Nov 27 '13 at 20:09
• Thank you, but it is still too slow for a video stream. I know, that I can use ColorSeparate, and then ImageMultiply and ImageSubtract. But I just think, that using Replacement rules or your suggestion is more elegant. – Simon Nov 27 '13 at 20:27
• If you want to use it for video streams, can you elaborate more on the way you process the frames? Are you importing frame after frame or do you import greater chunks? Maybe you import the whole video at once? Then there are faster ways which work on the complete stream. – halirutan Nov 27 '13 at 20:47

Here's a reasonably fast way:

p = Import[
"http://www.ultimatte-software.com/images/2FSN-TLW-wide03-ultimatte-software-blue.jpg"];

img1 = Image[(ImageData[p] /. {r_, g_, b_} -> {b} 2 - {g} - {r})]; //
AbsoluteTiming // First
(* 0.423668 *)

img2 = Image[ImageData[p].{-1, -1, 2}]; // AbsoluteTiming // First
(* 0.003273 *)

ImageSubtract[foo, foo2] // ImageData // Max
(* 2.22045*10^-16 *)


To get values between zero and 1, use Clip:

img3 = Image[Clip@ImageData[p].{-1., -1., 2.}]; //
AbsoluteTiming // First
(* 0.004997 *)


Extracting the image data and replacing the pixel-values with rules is definitely not the fastest way to go. A very neat way is to use ImageApply

ImageApply[2 #[[3]] - #[[2]] - #[[1]] &, p]


When you compare the two methods

Timing[Do[Image[(ImageData[p] /. {r_, g_, b_} -> {b} 2 - {g} - {r})], {20}]]
Timing[Do[ImageApply[2 #[[3]] - #[[2]] - #[[1]] &, p], {20}]]


you will see that ImageApply is about 10x faster than the replacement solution.

A very fast solution is the following which is about 40x faster

fc = Compile[{{p, _Real, 1}},
2 p[[3]] - p[[2]] - p[[1]],
CompilationTarget -> "C", Parallelization -> True,
RuntimeAttributes -> {Listable}];

Timing[Do[Image[fc@ImageData[p]], {20}]]

(* {0.360279, Null} *)

• This differs form OP's example with that the values are clipped to (0,1). Maybe he needs this, who knows :) – Kuba Nov 27 '13 at 20:38
• @Kuba Sometimes it's an advantage that ImageApply scales and clips the values, sometimes it's not.. – halirutan Nov 27 '13 at 20:40
• I know, I would use ImageApply. – Kuba Nov 27 '13 at 20:41

Image Type Matters

Though halirutan's parallel solution appears quite general, since his compiled function fc is capable to process lists of image data lists (as video processing was intended), it might remain highly dependent on the C compiler type applied. (As parallel processing was done, Timing is inappropriate to do runtime measurements - this will provide the sum of seconds of all respective parallel threads, it should always be done using AbsoluteTiming.)

Testing Michael E2's nice solution twenty times:

First@AbsoluteTiming@Do[Image[ImageData[p].{-1., -1., 2.}], {20}]


I have obtained:

0.226366

Including the clipping:

First@AbsoluteTiming@Do[Image[Clip@ImageData[p].{-1., -1., 2.}], {20}]


I get

0.315057

When I had a look at:

ImageType[p]


Byte

I thought utilizing this property both could give a further speed up and would include clipping:

First@AbsoluteTiming@Do[Image[ImageData[p, "Byte"].{-1, -1, 2}, "Byte"], {20}]


0.202920

Further tests using ColorSeparate and usual additions and subtractions gave only a slight improvement:

First@AbsoluteTiming@Do[Image[#3 + #3 - #1 - #2, "Byte"] &[
Sequence @@ Map[ImageData[#, "Byte"] &, ColorSeparate[p]]], {20}]


0.191750

So even on my slow machine a more or less inelegant solution by far would provide the capability for frame rate video processing, at least for frames of that size.

• Thank you very much for all your solutions, it was very helpful. I actually don't know, who to assign the answer? – Simon Nov 30 '13 at 14:53
• You should try out what suits best for your particular video processing framework (you will notice some platform dependencies), and then decide. As in many cases on this platforms, our solutions somehow depend on each other. – UDB Nov 30 '13 at 16:50