**Examples for Assignment 1** Luminance Operations ======================================================================== Brightness ------------------------------------------------------------------------ `brightnessFilter(image, ratio)` changes the brightness of an image by blending the original colors with black/white color in a ratio. (When `ratio > 0`, we blend with white to make it brighter; when `ratio < 0`, we blend with black to make it darker). ![-1](./images/brightness_-1.png border="1") ![-0.5](./images/brightness_-0.5.png border="1") ![0](./images/brightness_0.png border="1") ![0.5](./images/brightness_0.5.png border="1") ![1.0](./images/brightness_1.png border="1") Contrast ------------------------------------------------------------------------ `contrastFilter(image, ratio)` changes the contrast of an image by interpolating between a constant gray image (`ratio=-1`) with the average luminance and the original image (`ratio=0`). Interpolation reduces contrast, extrapolation boosts contrast, and negative factors generate inverted images. Use the following formula which is mentioned in [Wiki Contrast](http://en.wikipedia.org/wiki/Image_editing#Contrast_change_and_brightening): `value = (value - 0.5) * (tan ((contrast + 1) * PI/4) ) + 0.5;` ![-1](./images/contrast1.png border="1") ![-0.5](./images/contrast2.png border="1") ![0](./images/contrast3.png border="1") ![0.5](./images/contrast4.png border="1") ![1.0](./images/contrast5.png border="1") Gamma Correction ------------------------------------------------------------------------ `gammaFilter(image, logOfGamma)` changes the image by applying gamma correction, $V_{out} = V_{in} ^ \gamma$, where $\gamma = e^{\log{\gamma}}$ ![-1](./images/gamma1.png border="1") ![-0.4](./images/gamma2.png border="1") ![0](./images/gamma3.png border="1") ![0.4](./images/gamma4.png border="1") ![1.0](./images/gamma5.png border="1") Vignette ------------------------------------------------------------------------ `vignetteFilter(image, value)` darkens the corners of the image, as observed when using lenses with very wide apertures ([ref](http://en.wikipedia.org/wiki/Vignetting)). The function takes the innerRadius and outerRadius as inputs. The image should be perfectly clear up to `innerRadius`, perfectly dark (black) at `outerRadius` and beyond, and smoothly increase darkness in the circular ring in between. Both are specified as multiples of half the length of the image diagonal (so 1.0 is the distance from the image center to the corner). Note: the vignetting ring should be a perfect circle, not an ellipse. Camera lenses typically have circular apertures, even if the sensor/film is rectangular. ![innerR:0.25, outerR:1](./images/vignette1.png border="1") ![innerR:0.5, outerR:1](./images/vignette2.png border="1") ![innerR:0.25, outerR:0.75](./images/vignette3.png border="1") ![innerR:0, outerR:0.75](./images/vignette4.png border="1") Histogram Equalization ------------------------------------------------------------------------ `histeqFilter(image)` increase the contrast of the image by [histogram equalization](http://en.wikipedia.org/wiki/Histogram_equalization) in HSL’s L channel, that is, remapping the pixel intensities so that the final histogram is flat. A low contrast image usually clumps most pixels into a few tight clusters of intensities. Histogram equalization redistributes the pixel intensities uniformly over the full range of intensities [0, 1], while maintaining the relationship between light and dark areas of the image. ![Before](./images/histeq_before.jpg border="1" width="480") ![After](./images/histeq_after.png border="1" width="480") Color Operations ======================================================================== Saturation ------------------------------------------------------------------------ `saturationFilter(image, ratio)` changes the saturation of an image by interpolating between a gray level version of the image (`ratio=-1`) and the original image (`ratio=0`). Interpolation decreases saturation, extrapolation increases it, and negative factors preserve luminance but invert the hue of the input image. See [Graphica Obscura](http://graficaobscura.com/interp/index.html), its parameter `alpha=1+ratio` in our slider. ![-1](./images/saturation1.png border="1") ![-0.5](./images/saturation2.png border="1") ![0](./images/saturation3.png border="1") ![0.5](./images/saturation4.png border="1") ![1.0](./images/saturation5.png border="1") White Balance ------------------------------------------------------------------------ `whiteBalanceFilter(image, hex)` Adjust the [white balance](http://en.wikipedia.org/wiki/Color_balance) of the scene to compensate for lighting that is too warm, too cool, or tinted, to produce a neutral image. Use [Von Kries method](http://en.wikipedia.org/wiki/Color_balance#Von_Kries.27s_method): convert the image from RGB to the [LMS color space](http://en.wikipedia.org/wiki/LMS_color_space>) (there are several slightly different versions of this space, use any reasonable one, e.g. RLAB), divide by the LMS coordinates of the white point color (the estimated tint of the illumination), and convert back to RGB. ![Before correction: too warm](./images/white_balance_before.jpg border="1" width="360") ![After correction: neutral](./images/white_balance_after.jpg border="1" width="360") ![Given white hex: #cee2f5](./images/whiteBalance_cee2f5.png border="1" width="360") ![Given white hex: #f5cece](./images/whiteBalance_f5cece.png border="1" width="360") Historgram Matching ------------------------------------------------------------------------ `histMatchFilter = function(image, refImg)` Adjusts the color/contrast of the input `image` by matching the histgram to `refImg` images in the luminance channel. The results in the row below use the histogram matching in the luminance channel. ![reference image: town](./images/histmatch_gt1.png border="1" width="360") ![reference image: flower](./images/histmatch_gf1.png border="1" width="360") Filter Operations ======================================================================== Gaussian Filter ------------------------------------------------------------------------ `gaussianFilter(image, sigma)` Blurs an image by convolving it with a Gaussian filter. In the examples below, the Gaussian function used was $G(x) = e^{\frac{-x^2}{2\sigma^2}}$ and the number below each image indicates the sigma of the filter. You set the filter window size to `Math.round(3*sigma)*2+1`. ![1](./images/gaussian1.png border="1") ![2](./images/gaussian2.png border="1") ![3](./images/gaussian3.png border="1") ![4](./images/gaussian4.png border="1") ![5](./images/gaussian5.png border="1") Sharpen Filter ------------------------------------------------------------------------ `sharpenFilter(image)` sharpens edges in an image by convolving it with the edge kernel as belows and add it to the original image: \begin{bmatrix} -1 & -1 & -1 \\ -1 & 8 & -1 \\ -1 & -1 & -1 \end{bmatrix} ![](./images/sharpen.png border="1" width="480") Edge ------------------------------------------------------------------------ `edgeFilter = function(image)` convolves the image with the edge kernel. We invert the image (`pixel = 1 - pixel`) in the example below for better visualization. ![](./images/edge1.png border="1" width="480") ![](./images/edge2.png border="1" width="480") Median Filter ------------------------------------------------------------------------ `medianFilter(image, winR)` blurs an image by replacing each pixel by the median of its neighboring pixel(`(2*winR+1) * (2*winR+1)`). The results below are generated by doing median filter in RGB channel separately. You can also sort the pixels using the luminance only. ![1](./images/median1.png border="1") ![2](./images/median2.png border="1") ![3](./images/median3.png border="1") ![4](./images/median4.png border="1") ![5](./images/median5.png border="1") Bilateral Filter ------------------------------------------------------------------------ `bilateralFilter(image, sigmaR, sigmaS)` blurs an image by replacing each pixel by a weighted average of nearby pixels. The weights depend not only on the euclidean distance of pixels but also on the pixel difference, for the pixel difference it could either be luminance difference or L2 distance in color space. Consider the pixel I(i,j) located in (i,j), the weight of pixel I(k,l) follows the following equation: ![](http://upload.wikimedia.org/math/9/7/1/97120010aa3bf0e04d5827492f184eba.png) We set the filter window size to `2* Math.round( max(sigmaR,sigmaS)*2 ) + 1`. In the examples below, to make the weight for pixel and spatial more fair, we multiply `sigmaR` by `sqrt(2)*winR`. If we don't take this factor into consideration, the filter does not do any blurring and the result looks unchanged. ![sigmaR=1, sigmaS=1](./images/bilateral1.png border="1") ![sigmaR=2, sigmaS=1](./images/bilateral2.png border="1") ![sigmaR=3, sigmaS=0.5](./images/bilateral3.png border="1") ![sigmaR=4, sigmaS=2](./images/bilateral4.png border="1") ![sigmaR=5, sigmaS=3](./images/bilateral5.png border="1") Dithering Operations ======================================================================== Quantization ------------------------------------------------------------------------ `quantizeFilter(image)` converts an image to a binary image (0 or 1). ![](./images/quantize_binary.png border="1" width="480") Random Dither ------------------------------------------------------------------------ `randomFilter(image)` converts an image to a binary image using random dithering. It is similar to uniform quantization, but random noise range in each unit is added to each component during quantization, so that the arithmetic mean of many output pixels with the same input level will be equal to this input level. ![](./images/random_binary.png border="1" width="480") Floyd-Steinberg Dither ------------------------------------------------------------------------ `floydFilter(image)` converts an image to a binary image using Floyd-Steinberg dither with error diffusion. Each pixel (_x_,_y_) is quantized, and the quantization error is computed. Then the error is diffused to the neighboring pixels (_x_ + 1,_y_), (_x_ - 1,_y_ + 1), (_x_,_y_ + 1), and (_x_ + 1,_y_ + 1) , with weights 7/16, 3/16, 5/16, and 1/16, respectively. ![](./images/floyd_binary.png border="1" width="480") Ordered Dither ------------------------------------------------------------------------ `orderedFilter(image)` converts an image to a binary image using ordered dithering. The following examples used the pattern: \begin{bmatrix} 15 & 7 & 13 & 5 \\ 3 & 11 & 1 & 9 \\ 12 & 4 & 14 & 6 \\ 0 & 8 & 2 & 10 \end{bmatrix} The values can be used as thresholds for rounding up or down as described in the lecture slides. ![](./images/ordered_binary.png border="1" width="480") Resampling Operations ======================================================================== Scale ------------------------------------------------------------------------ `scaleFilter(image, ratio)` scales an image in width and height by ratio. The result depends on the current sampling method (point, bilinear, or Gaussian). In the example below, gamma=1, the window radius of the Gaussian filter is 3, ratio = 0.7. ![Point](./images/scale1.png border="1") ![Bilinear](./images/scale2.png border="1") ![Gaussian](./images/scale3.png border="1") Rotate ------------------------------------------------------------------------ `rotateFilter(image, radians, sampleMode)` rotates an image by the given angle, in radians (a positive angle implies clockwise rotation). The result depends on the current sampling method (point, bilinear, or Gaussian). We set sigma of the gaussian filter to 1.0, and the window radius of the Gaussian filter to 3.0. \ In the example below, radians = 0.2 * pi. ![Point](./images/rotate1.png border="1") ![Bilinear](./images/rotate2.png border="1") ![Gaussian](./images/rotate3.png border="1") Swirl ------------------------------------------------------------------------ `swirlFilter(image, radians, sampleMode)` warps an image using a creative filter of your choice. In the following example, each pixel is mapped to its corresponding scaled polar coordinates, here radians = 0.4 * pi. ![Point](./images/swirl1.png border="1") ![Bilinear](./images/swirl2.png border="1") ![Gaussian](./images/swirl3.png border="1") Composite Operations ======================================================================== Composite ------------------------------------------------------------------------ `compositeFilter(backgroundImg, foregroundImg)` composites the foreground image over the background image, using the alpha channel of the foreground image to blend two images. The alpha channel can be obtained by pushing a third image or painting a third image using "Brush". Gaussian smoothing the painted alpha channel usually gives better result. ![Background Img](./images/comp_background.jpg border="1") ![Foreground Img](./images/comp_foreground.jpg border="1") ![Foreground Img(alpha channel)](./images/comp_mask.jpg border="1") ![Result](./images/comp_result.jpg border="1") ![Background Img](./images/man.jpg border="1") ![Foreground Img](./images/doge.jpg border="1") ![Foreground Img(alpha channel)](./images/alpha.png border="1") ![Result](./images/composite.png border="1") Morph ------------------------------------------------------------------------ `morphFilter(initialImg, finalImg, lines, alpha) ` Morph two images using [[Beier92]](http://www.hammerhead.com/thad/morph.html). `initialImg` and `finalImg` are the before and after images, respectively. `lines` are corresponding line segments to be aligned. `alpha` is the morph time: it can be a number between 0 and 1 indicating which point in the morph sequence should be returned, or can be (start:step:end) to define a morph sequence. In terms of parameter choosing, we set p = 0.5, a = 0.01, and b = 2. ![0](./images/morphBO_01.jpg border="1") ![0.11](./images/morphBO_02.jpg border="1") ![0.22](./images/morphBO_03.jpg border="1") ![0.33](./images/morphBO_04.jpg border="1") ![0.44](./images/morphBO_05.jpg border="1") ![0.56](./images/morphBO_06.jpg border="1") ![0.67](./images/morphBO_07.jpg border="1") ![0.78](./images/morphBO_08.jpg border="1") ![0.89](./images/morphBO_09.jpg border="1") ![1.0](./images/morphBO_10.jpg border="1") Here is an animation of the sequence: ![](./images/morphBO.gif border="1") And here is the morph for parameters `alpha=(0:0.1:1)` for the example images and morph lines provided with the assignment zip, together with a still frame at `alpha=0.5`: ![](./images/morph-trump.gif border="1") ![](./images/morph-trump.png border="1") Miscellaneous ======================================================================== Palette ------------------------------------------------------------------------ `paletteFilter(image, colorNum)` extracts colorNum colors as a palette to represent colors in the image. Here we use k-means method to extract color palette in the image with grid acceleration. ![colorNum = 2](./images/palette1.png border="1") ![colorNum = 3](./images/palette2.png border="1") ![colorNum = 5](./images/palette1.png border="1") XDoG ------------------------------------------------------------------------ `xDoGFilter(image, value)` Stylizes images into a pencil-drawing fashion using the eXtended Difference-of-Gaussians compendium described by Winnemoeller. You can use the approach of Kang to make the flow field (instead of the one in the XDoG paper) because it is based on bilateral filter that you already implemented. ![0.75](./images/xDoG_0.75.png border="1" width="480") ![1.0](./images/xDoG_1.0.png border="1" width="480")