First off, I tried to make Patrick from Spongebob sharper, because sometimes he's not that sharp.
Doesn't Giselle Bundchen look great?
At 2AM, I thought it would be a great idea to make Pillsbury Dough Boy dance in the sky in the background of a panorama where I look pretty pensive. You know, to lighten the mood. I ripped the source images from the frames of a youtube video and created a bunch of masks on Photoshop.
To reconstruct the toy image (size mxn), I first made two sparse matrices that represented the constraints for the x and the y gradient. The x gradient is mxn equations representing the intensity difference of the pixel with the pixel to the left of it. The y gradient is mxn equations representing the intensity difference of the pixel with the pixel above it. I added one last equation to make sure that the color of a select pixel matched the intensity of the original image. Putting all these equations together, I got an overconstrained system that I could solve for using least squares approximation.
Gradient blending requires a source, a target, and a mask. The algorithm uses least squares (Ax = b) to solve for the pixel intensity of the white areas in the mask (x).
First I created a sparse matrix that was ((mxn, mxn)) to represent A, the coefficient matrix of the system of equations. This matrix was only nonzero on the diagonal and in the four neighborhoods around the masked indices. The diagonals were nonzero because each pixel has an intensity to be solved for, even if the pixel is outside the mask and the value comes directly from the target image.
The 'four neighborhoods' were represented as coefficients/rows in the sparse matrix, with a 4 at the masked pixel and -1's for every neighbor also in the mask.
Then I constructed the 'b' vector either by computing the four neighborhood from the correct image or copying the value over from the target image (if the pixel was outside the mask). By the four neighborhood from the correct image, I mean the src image if the entire four neighborhood around the masked pixel is also in the mask and the target image otherwise.
The challenging part about this implementation was that it could get computationally expensive really fast depending on the size of the mask, so the code had to be vectorized.
I titled it Hive Gave Me Butterflies, because Hive9, Hive11, and Hive30 really worked hard for me this weekend. Also, you get pretty antsy when your program runs for a long time.
You can see above here the type of mask, source image, and target image I used to construct this, as well as how it looks just copy and pasted. Each color channel took approximately 20-40 seconds.
A failure case: I believe the reason why this one failed is because the gradients (the curtains, the backyard my dog came from) were too dissimilar to match properly.
Another failure case: I think this one failed because of the same deal, the lighting on the left side of the face is more dramatic and contrasted too much with the even, well-lit nature of the source image.
I wasn't really impressed with the previous two images (the ballerina and the contemporary dancer), so I thought I might as well Laplacian blend the Poisson outputs together to see if I could create a better manipulation. The nice thing to note is that the clouds copied over so nicely from the ballerina image that I had to look quite carefully to notice the difference!
Putting speed aside (Poisson blending took far longer), the Laplacian blending preserved the color of the source image better even when the source and the target had too much contrast by the boundaries. As we can see in these two images (which were fails for Poisson), the luminance of my dog and Constance Wu (the src images) are unaffected by the dark backdrop of the curtains (left) and the dramatic lighting (right).
However, the tradeoff is that the target and the source no longer blend as seamlessly. For my dog, you can still see specters of the background he came from.
Basically, Poisson blending is worse when the gradients and the boundaries are drastically different from each other. Laplacian blending is worse when you really want the seamless transition between source and target that Poisson guarantees.