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!
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!
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:
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]
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]
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
Mathematica has a few very limited 'effects' built-in:
i = Import["https://i.sstatic.net/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...
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"]
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 ;).
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.