34
$\begingroup$

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

Take the following image as an example:Example Image

Thank you so much if you could help!

$\endgroup$
9
  • 4
    $\begingroup$ Probably the way to do it is to read all of these papers on painterly rendering and implement one of them in Mathematica. $\endgroup$
    – user484
    Commented Dec 18, 2013 at 4:57
  • 3
    $\begingroup$ Any special reason to do it with Mathematica? $\endgroup$ Commented Dec 18, 2013 at 5:35
  • 4
    $\begingroup$ Many raster image processing programs come with a filter similar to the one you request. Adobe Photoshop is an example of one such. $\endgroup$
    – m_goldberg
    Commented Dec 18, 2013 at 6:38
  • $\begingroup$ 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. $\endgroup$
    – Mr.Wizard
    Commented Dec 18, 2013 at 6:58
  • $\begingroup$ There is probably a newer, commercial version of this type of filter now but I'm not familiar with it. $\endgroup$
    – Mr.Wizard
    Commented Dec 18, 2013 at 6:59

5 Answers 5

53
$\begingroup$

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["https://i.sstatic.net/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:

enter image description here

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 = Image`ToneMapHDRI[img, Method -> {"AdaptiveLog", "Bias" -> 1.0}];

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

enter image description here

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["https://i.sstatic.net/XwYg7.jpg"];
tm = Image`ToneMapHDRI[img, Method -> {"AdaptiveLog"}];
edges = ColorNegate[
 img ~ImageEffect~ {"GaussianNoise", 0.1} ~GradientFilter~ 3] ~ImageAdjust~ {0.2, 0, 5};

ImageMultiply[tm, edges]

enter image description here

$\endgroup$
7
  • $\begingroup$ 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... $\endgroup$
    – cormullion
    Commented Dec 19, 2013 at 7:43
  • $\begingroup$ @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. $\endgroup$ Commented Dec 19, 2013 at 21:30
  • $\begingroup$ Like it. Next time let's hope the questioner asks for Seurat or Mondrian or Vaserely... :) $\endgroup$
    – cormullion
    Commented Dec 19, 2013 at 22:30
  • $\begingroup$ 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) $\endgroup$
    – Peltio
    Commented Dec 19, 2013 at 22:31
  • $\begingroup$ Results from GradientOrientationFilter look really nice :)! $\endgroup$
    – Kardashev3
    Commented Dec 20, 2013 at 6:37
15
$\begingroup$

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

i = Import["https://i.sstatic.net/XwYg7.jpg"];
oil = ImageEffect[i, {"OilPainting", 4}]

oil

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

embossed

Combining them:

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

combined

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

$\endgroup$
3
  • 3
    $\begingroup$ Soaking your left ear in absinthe while drinking it may get better results yet. $\endgroup$ Commented Dec 18, 2013 at 11:50
  • 1
    $\begingroup$ @belisarius You go to some interesting parties... $\endgroup$
    – cormullion
    Commented Dec 18, 2013 at 11:51
  • $\begingroup$ Those aren't my parties! $\endgroup$ Commented Dec 18, 2013 at 12:11
13
$\begingroup$

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["https://i.sstatic.net/XwYg7.jpg"]

Mathematica graphics

Now we smooth is nicely with the CurvatureFlowFilter:

imgsmooth = CurvatureFlowFilter[img, 2]

Mathematica graphics

And generate a gradient image of the original image:

grad = ImageAdjust@GradientFilter[img, 1]

Mathematica graphics

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]

Mathematica graphics

Finally we compose the final image:

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

Mathematica graphics

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

$\endgroup$
2
  • $\begingroup$ Wow! Nicely done. $\endgroup$
    – Gabriel
    Commented Dec 18, 2013 at 17:43
  • $\begingroup$ Fancy!!! Thank you! $\endgroup$
    – user10495
    Commented Dec 18, 2013 at 18:47
13
$\begingroup$

Now there's a built-in function for this called ImageRestyle

enter image description here

$\endgroup$
1
  • 1
    $\begingroup$ Very cool result - thanks for suggesting this built in function! $\endgroup$
    – Mark R
    Commented Feb 25, 2020 at 3:09
9
$\begingroup$

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

(components)

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}]

masks

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

brush stroke 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]

final image

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.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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