I’ve always loved impressionist paintings, but I don’t think I could make one. My computer might have more artistic talent than I do, though.
Non-photorealistic-rendering is the technical term for what I wanted to do. A painting is a large number of brush strokes scaled and oriented in a way that artfully mimics a scene. With the right approach, we can do this automatically to recreate any image!
We start with a target image and a brush stroke image. Our basic plan will be to composite a large number of brush stroke images together until they resemble our target image.
Human painters start with a base coat that fills in the background and then fill in the details; we’ll try to do something similar by painting our image in two distinct passes. The first pass isn’t too fancy; we place brush stroke images at random at their maximum size and set their color to be the same as the target image. The second pass is more critical. We need to:
- Chose the color of our strokes
- Chose the locations of our strokes
- Chose the size of our strokes
- Chose the orientation of our strokes
The first item is fairly straightforward, but the rest require some more thought. We want to place more strokes in areas with sharp changes in color. We also want to make those strokes smaller and orient them along edges.
To do this, we generate the structure tensor for our image and calculate the eigenvectors/eigenvalues at each pixel. The eigenvector associated with the smallest eigenvalue represents the direction of least change (i.e our edge directions). The greatest eigenvalue represents the strength of the gradient at that point (i.e how small our stroke should be). The greatest eigenvalue also tells us how often we should sample at that point.
This gives us all the info we need to start painting. Here are some of the results!
We can also change up our brush stroke image to get different painting styles.