# How to create effect like Van Gogh's stroke brush?

I need to apply Van Gogh's stroke brush effect to a random image

Take the following image as an example:

Thank you so much if you could help!

-
Probably the way to do it is to read all of these papers on painterly rendering and implement one of them in Mathematica. –  Rahul Dec 18 '13 at 4:57
Any special reason to do it with Mathematica? –  belisarius Dec 18 '13 at 5:35
Many raster image processing programs come with a filter similar to the one you request. Adobe Photoshop is an example of one such. –  m_goldberg Dec 18 '13 at 6:38
You will probably end up doing this in a photo editor. Some old filter exist that could help you. Fantastic Machines PaintEngine (beta) is free and simple, but not particularly powerful. The Impressionist filter from Microsoft Image Composer does a better job; I think it used to be freely available but I cannot find a copy now. There was also a filter called "Xaos Tools Paint Alchemy" which I think was similar to Impressionist; it may be "abandonware" or simply unavailable. –  Mr.Wizard Dec 18 '13 at 6:58
There is probably a newer, commercial version of this type of filter now but I'm not familiar with it. –  Mr.Wizard Dec 18 '13 at 6:59

## 4 Answers

Edit

Here is a different approach using Graphics to actually draw some brush strokes. I run a GradientOrientationFilter on a smaller version of the image to estimate the local image gradient, and use that information to create a collection of randomly shaded lines:

img = Import["http://i.stack.imgur.com/XwYg7.jpg"];

im = img ~ImageResize~ 200 ~ColorConvert~ "Grayscale";

gof = im ~GradientOrientationFilter~ 5;

lines = MapIndexed[{GrayLevel@RandomReal[],
Line[{#2 - 2 {Cos[#1], Sin[#1]}, #2 + 2 {Cos[#1], Sin[#1]}}]} &,
Reverse /@ Transpose@ImageData@gof, {2}];

brush = Image[
Graphics[lines,
PlotRange -> {{1, #1}, {1, #2}} & @@ ImageDimensions[im],
Background -> GrayLevel[0.5], ImageSize -> ImageDimensions[img]],
ColorSpace -> "Grayscale"];


Here is the brush image:

I use a gentle tone-mapping on the original image to equalise the brightness a bit, and then combine the ton-mapped image with the brush strokes.

tm = ImageToneMapHDRI[img, Method -> {"AdaptiveLog", "Bias" -> 1.0}];

Image[0.7 (ImageData@Blur[brush, 2] - 0.6) + ImageData@tm]


You can vary the effect by altering parameters such as the scale of the gradient orientation filter and the length of the lines.

Original

Here's an attempt using tone mapping to equalise the brightness across the image, and a gradient filter to enhance outlines. I added some noise to the image before doing the gradient filter, to try and get some variation in the sky. I found it quite difficult to avoid highlighting the jpeg artefacts in the sky.

img = Import["http://i.stack.imgur.com/XwYg7.jpg"];
tm = ImageToneMapHDRI[img, Method -> {"AdaptiveLog"}];
edges = ColorNegate[
img ~ImageEffect~ {"GaussianNoise", 0.1} ~GradientFilter~ 3] ~ImageAdjust~ {0.2, 0, 5};

ImageMultiply[tm, edges]


-
Looks nice - the sky is a problem as you say, just not very van-goghy to start with .. It probably needs some of your hedcut-style texturing... –  cormullion Dec 19 '13 at 7:43
@cormullion, I wondered if ListLineIntegralConvolutionPlot might help with texturing, but it's so slow! I decided to just draw some straight lines with plain old Line primitives and it came out quite nicely. –  Simon Woods Dec 19 '13 at 21:30
Like it. Next time let's hope the questioner asks for Seurat or Mondrian or Vaserely... :) –  cormullion Dec 19 '13 at 22:30
Your second attempt looks very promising! Perhaps you should find a way to make the randomly shaped brushes depend in a stronger way on the gradient of the image... The twoer and the sky seems to be made with the same brush stroke. Maybe by using a much more contrasted image as a base for that? (Just guessing) –  Peltio Dec 19 '13 at 22:31
Results from GradientOrientationFilter look really nice :)! –  Kardashev3 Dec 20 '13 at 6:37

Mathematica has a few very limited 'effects' built-in:

i = Import["http://i.stack.imgur.com/XwYg7.jpg"];
oil = ImageEffect[i, {"OilPainting", 4}]


boss = ImageEffect[i, {"Embossing", 15}]


Combining them:

ImageAdjust[ImageCompose[boss, {oil, .5}], {1, 0}]


But for the best effects, you'll have to start drinking absinthe...

-
Soaking your left ear in absinthe while drinking it may get better results yet. –  belisarius Dec 18 '13 at 11:50
@belisarius You go to some interesting parties... –  cormullion Dec 18 '13 at 11:51
Those aren't my parties! –  belisarius Dec 18 '13 at 12:11

You can achieve an effect like this by using CurvatureFlowFilter and GaborFilter. The effects are best viewed in full resolution.

Here is the original image:

img = Import["http://i.stack.imgur.com/XwYg7.jpg"]


Now we smooth is nicely with the CurvatureFlowFilter:

imgsmooth = CurvatureFlowFilter[img, 2]


And generate a gradient image of the original image:

grad = ImageAdjust@GradientFilter[img, 1]


Using the gradient image we now compute an image based on a series of GaborFilters:

gab = ParallelTable[
ImageAdjust[GaborFilter[grad, {40, 7}, {Sin[n], Cos[n]}]], {n, 0,
Pi, 0.1}];
dirs = ImageApply[Max, gab]


Finally we compose the final image:

res = ImageAdjust[ImageCompose[dirs, {imgsmooth, .5}], {1, 0}]


Simply tune the parameters a bit and adjust them to your needs - Van Gogh would certainly be jealous ;).

-
Wow! Nicely done. –  Gabriel Dec 18 '13 at 17:43
Fancy!!! Thank you! –  user10495 Dec 18 '13 at 18:47

I see the "Van Gogh" style as a combination of brush strokes and an averaging filter. This is the best result of my effort to simulate this style. I'm sure someone with better knowledge can improve it.

First, we try to split the picture to regions that will have the same brush stroke pattern.

orig = Image[ImageData[ImageResize[img, 500]][[All, All, 1 ;; 3]]];(* get rid of the alpha channel which would cause trouble later on *)

wsc = WatershedComponents[GradientFilter[orig, 5],
MaxDetect[GaussianFilter[orig, 30], Padding -> 1]];
(wsc[[Sequence @@ #]] = wsc[[Sequence @@ (# - {0, 1})]]) & /@
Position[wsc, 0];
wsc // Colorize


()

Next, we select significant parts and create masks for them:

partAreas = Count[Flatten[wsc], #] & /@ Range[Max[wsc]];
significantParts =
Flatten[Position[partAreas, _?(# > Max[partAreas]/200 &)]];
masks = Table[
Image[Map[DiscreteDelta[n - #] &, wsc, {2}]], {n, significantParts}]


Then we create brush strokes:

brushMasks = (xr = RandomInteger[{1, 10}];
yr = RandomInteger[{1, 10}];
strPlot =
StreamPlot[{xr + Sin[2 + (x - y)*5],
yr + Sin[2 + (x + y)*5]}, {x, -3, 3}, {y, -3, 3},
StreamScale -> {.05, .1, 0.001},
StreamPoints -> {2000, .01, .01}, PlotRange -> All,
PlotRangePadding -> None, Frame -> False,
StreamStyle ->
Directive[{GrayLevel[.7], CapForm["Round"],
AbsoluteThickness[3]}], ImageSize -> ImageDimensions[orig],
AspectRatio -> Divide @@ Reverse[ImageDimensions[orig]]];
GaussianFilter[ImageMultiply[#, Rasterize[strPlot]], 2]) & /@
masks


Finally, we apply masks on our image and combine them together:

maskedParts =
Image[Map[Clip[#, {0, 1}] &,
ImageData[ImageSubtract[orig, ColorNegate[#]]], {3}]] & /@
brushMasks
brushedImage = maskedParts[[1]];
n = 2;
While[n <= Length[maskedParts],
brushedImage = ImageAdd[brushedImage, maskedParts[[n]]]; n++];
MedianFilter[brushedImage, 1]


There are two issues with this method:

1) Brush strokes take a very long time to generate with my method (using StreamPlot); I'm sure there's a better way.

2) The borders between the components can be seen in the final image. I'm not sure how to avoid this.

-