[Question]
  [
As any *amateur* photographer can tell you, extreme post-processing is **always good.** One such technique is called "[miniature-faking](http://en.wikipedia.org/wiki/Miniature_faking)". 
The object is to make an image look like a photograph of a miniaturized, toy version of itself. This works best for photographs taken from a moderate/high angle to the ground, with a low variance in subject height, but can be applied with varying effectiveness to other images.
**The challenge:** Take a photograph and apply a miniature-faking algorithm to it. There are many ways to do this, but for the purposes of this challenge, it boils down to:
* **Selective blur**
> 
> Some part of the image should be blurred to simulate a shallow depth-of-field. This is usually done along some gradient, whether linear or shaped. Choose whatever blur/gradient algorithm you like, but between 15-85% of the image must have "noticeable" blur.
> 
> 
>
* **Saturation boost**
> 
> Pump up the color to make things appear they were painted by hand. Output must have an average saturation level of > +5% when compared to input. (using [HSV saturation](http://en.wikipedia.org/wiki/HSL_and_HSV#Saturation))
> 
> 
>
* **Contrast Boost**
> 
> Increase the contrast to simulate harsher lighting conditions (such as you see with an indoor/studio light rather than the sun). Output must have a contrast of > +5% when compared to input. (using [RMS algorithm](http://en.wikipedia.org/wiki/Contrast_%28vision%29#RMS_contrast))
> 
> 
>
Those three alterations **must** be implemented, and **no other** enhancements/alterations are allowed. No cropping, sharpening, white balance adjustments, nothing.
* Input is an image, and can be read from a file or memory. You may use external libraries to *read and write* the image, but you **cannot** use them to *process* the image. Supplied functions are also disallowed for this purpose (you can't just call `Image.blur()` for example)
* There is no other input. The processing strengths, levels, etc, must be determined by the program, not by a human.
* Output can be displayed or saved as a file in a standardized image format (PNG, BMP, etc).
* Try to generalize. It should not work on only *one* image, but it's understandable that it won't work on *all* images. Some scenes simply do not respond well to this technique, no matter how good the algorithm. Apply common sense here, both when answering and voting on answers.
* Behavior is undefined for invalid inputs, and those images which are impossible to satisfy the spec. For example, a grayscale image cannot be saturated (there is no base hue), a pure white imaged cannot have increased contrast, etc.
* Include at least two output images in your answer:
> 
> One must be generated from one of the images in [this dropbox folder](https://www.dropbox.com/sh/gc9zz3dg3lxg6ou/RNVpbzNnPW). There are six to choose from, but I tried to make them all viable to varying degrees. You can see sample outputs for each in the `example-outputs` subfolder. Please note that these are full 10MP JPG images straight out of the camera, so you have a lot of pixels to work on.
> 
> 
> The other can be any image of your choice. Obviously, try to choose images that are freely usable. Also, include either the original image or a link to it for comparison. 
> 
> 
>
---
For example, from this image:
![original](https://i.stack.imgur.com/jUZYQ.png)
You might output something like:
![processed](https://i.stack.imgur.com/rieky.png)
For reference, the example above was processed in GIMP with an angular box-shaped gradient gaussian blur, saturation +80, contrast +20. (I don't know what units GIMP uses for those)
For more inspiration, or to get a better idea what you're trying to achieve, check out [this site](http://benthomas.net.au/), or [this one](http://www.uniqlo.com/calendar/). You can also search for `miniature faking` and `tilt shift photography` for examples.
---
This is a popularity contest. Voters, vote for the entries you feel look the best while staying true to the objective. 
---
**Clarification:**
Clarifying what functions are disallowed, it was not my intent to ban *math* functions. It was my intent to ban *image manipulation* functions. Yes, there's some overlap there, but things like FFT, convolutions, matrix math, etc, are useful in many other areas. You should *not* be using a function that simply takes an image and blurs. If you find a suitably mathy way to *create* a blur, that fair game.
  
[Answer]
## Java : Reference Implementation
Here's a basic reference implementation in Java. It works best on high-angle shots, and it's horribly inefficient.
The blur is a very basic box blur, so it loops over the same pixels much more than necessary. The contrast and saturation *could* be combined into a single loop also, but the vast majority of the time spent is on blur, so it wouldn't see much gain from that. That being said, it works fairly quickly on images under 2MP or so. The 10MP image took some time to complete.
The blur quality could easily be improved by using *basically anything* but a flat box blur. The contrast/saturation algorithms do their job, so no real complaints there.
There is no real intelligence in the program. It uses constant factors for the blur, saturation, and contrast. I played around it with it to find happy medium settings. As a result, there *are* some scenes that it doesn't do very well. For instance, it pumps the contrast/saturation so much that images with large similarly-colored areas(think sky) break up into color bands.
It's simple to use; just pass the file name in as the only argument. It outputs in PNG regardless of what the input file was.
### Examples:
**From the dropbox selection:**
These first images are scaled down for ease of posting. Click the image to see full-size.
After:
[![enter image description here](https://i.stack.imgur.com/DAz2b.jpg)](https://www.dropbox.com/s/614yodz9js31m18/loop_large_post.png)
Before:
[![enter image description here](https://i.stack.imgur.com/8mC3M.jpg)](https://www.dropbox.com/s/3mlk6ixs1gs0aec/loop_large.jpg)
**Miscellaneous selection:**
After:
![enter image description here](https://i.stack.imgur.com/JLLna.png)
Before:
![enter image description here](https://i.stack.imgur.com/IJOSR.jpg)
```
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class MiniFake {
    int maxBlur;
    int maxDist;
    int width;
    int height;
    
    public static void main(String[] args) {
        if(args.length < 1) return;
        new MiniFake().run(args[0]);
    }
    void run(String filename){
        try{
            BufferedImage in = readImage(filename);
            BufferedImage out = blur(in);
            out = saturate(out, 0.8);
            out = contrast(out, 0.6);
            
            String[] tokens = filename.split("\\.");
            String outname = tokens[0];
            for(int i=1;i<tokens.length-1;i++)
                outname += "." + tokens[i];
            ImageIO.write(out, "png", new File(outname + "_post.png"));
            System.out.println("done");
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    
    BufferedImage contrast(BufferedImage in, double level){
        BufferedImage out = copyImage(in);
        long lumens=0;
        for(int x=0;x<width;x++)
            for(int y=0;y<height;y++){
                int color = out.getRGB(x,y);
                lumens += lumen(getR(color), getG(color), getB(color));
            }
        lumens /= (width * height);
        
        for(int x=0;x<width;x++)
            for(int y=0;y<height;y++){
                int color = out.getRGB(x,y);
                int r = getR(color);
                int g = getG(color);
                int b = getB(color);
                double ratio = ((double)lumen(r, g, b) / (double)lumens) - 1d;
                ratio *= (1+level) * 0.1;
                r += (int)(getR(color) * ratio+1);
                g += (int)(getG(color) * ratio+1);
                b += (int)(getB(color) * ratio+1);
                out.setRGB(x,y,getColor(clamp(r),clamp(g),clamp(b)));
            }   
        return out;
    }
    
    BufferedImage saturate(BufferedImage in, double level){
        BufferedImage out = copyImage(in);
        for(int x=0;x<width;x++)
            for(int y=0;y<height;y++){
                int color = out.getRGB(x,y);
                int r = getR(color);
                int g = getG(color);
                int b = getB(color);
                int brightness = Math.max(r, Math.max(g, b));
                int grey = (int)(Math.min(r, Math.min(g,b)) * level);
                if(brightness == grey)
                    continue;
                r -= grey;
                g -= grey;
                b -= grey;
                double ratio = brightness / (double)(brightness - grey);
                r = (int)(r * ratio);
                g = (int)(g * ratio);
                b = (int)(b * ratio);
                out.setRGB(x, y, getColor(clamp(r),clamp(g),clamp(b)));
            }
        return out;
    }
    
    
    BufferedImage blur(BufferedImage in){
        BufferedImage out = copyImage(in);
        int[] rgb = in.getRGB(0, 0, width, height, null, 0, width);
        for(int i=0;i<rgb.length;i++){
            double dist = Math.abs(getY(i)-(height/2));
            dist = dist * dist / maxDist;
            int r=0,g=0,b=0,p=0;
            for(int x=-maxBlur;x<=maxBlur;x++)
                for(int y=-maxBlur;y<=maxBlur;y++){
                    int xx = getX(i) + x;
                    int yy = getY(i) + y;
                    if(xx<0||xx>=width||yy<0||yy>=height)
                        continue;
                    int color = rgb[getPos(xx,yy)];
                    r += getR(color);
                    g += getG(color);
                    b += getB(color);
                    p++;
                }
            
            if(p>0){
                r /= p;
                g /= p;
                b /= p;
                int color = rgb[i];
                r = (int)((r*dist) + (getR(color) * (1 - dist)));
                g = (int)((g*dist) + (getG(color) * (1 - dist)));
                b = (int)((b*dist) + (getB(color) * (1 - dist)));
            } else {
                r = in.getRGB(getX(i), getY(i));
            }
            out.setRGB(getX(i), getY(i), getColor(r,g,b));
        }
        return out;
    }
    BufferedImage readImage(String filename) throws IOException{
         BufferedImage image = ImageIO.read(new File(filename));
         width = image.getWidth();
         height = image.getHeight();
         maxBlur = Math.max(width, height) / 100;
         maxDist =  (height/2)*(height/2);
         return image;
    }
    
    public BufferedImage copyImage(BufferedImage in){
        BufferedImage out = new BufferedImage(in.getWidth(), in.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics g = out.getGraphics();
        g.drawImage(in, 0, 0, null);
        g.dispose();
        return out;
    }
    static int clamp(int c){return c<0?0:c>255?255:c;}
    static int getColor(int a, int r, int g, int b){return (a << 24) | (r << 16) | (g << 8) | (b);}
    static int getColor(int r, int g, int b){return getColor(0xFF, r, g, b);}
    static int getR(int color){return color >> 16 & 0xFF;}
    static int getG(int color){return color >> 8 & 0xFF;}
    static int getB(int color){return color & 0xFF;}
    static int lumen(int r, int g, int b){return (r*299)+(g*587)+(b*114);}
    int getX(int pos){return pos % width;}
    int getY(int pos){return pos / width;}
    int getPos(int x, int y){return y*width+x;} 
}
```
[Answer]
## C#
Instead of doing any iterative box blurs, I decided to go the entire way and write a Gaussian blur. The `GetPixel` calls really slow it down when using large kernels, but it's not really worthwhile to convert the methods to use `LockBits` unless we were processing some larger images.
Some examples are below, which use the default tuning parameters I set (I didn't play with the tuning parameters much, because they seemed to work well for the test image).
For the test case provided...
![1-Original](https://i.stack.imgur.com/ZwctM.png)
![1-Modified](https://i.stack.imgur.com/CesEv.jpg)
Another...
![2-Original](https://i.stack.imgur.com/NoCPJ.jpg)
![2-Modified](https://i.stack.imgur.com/Gk6kx.jpg)
Another...
![3-Original](https://i.stack.imgur.com/TQ8x0.jpg)
![3-Modified](https://i.stack.imgur.com/Vkac4.jpg)
The saturation and contrast increases should be fairly straightforward from the code. I do this in the HSL space and convert it back to RGB.
The [2D Gaussian kernel](http://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function) is generated based on the size `n` specified, with:
```
EXP(-((x-x0)^2/2+(y-y0)^2/2)/2)
```
...and normalized after all kernel values are assigned. Note that `A=sigma_x=sigma_y=1`.
In order to figure out where to apply the kernel, I use a blur weight, calculated by:
```
SQRT([COS(PI*x_norm)^2 + COS(PI*y_norm)^2]/2)
```
...which gives a decent response, essentially creating an ellipse of values that are protected from the blur that gradually fades further out. A band-pass filter combined with other equations (maybe some variant of `y=-x^2`) could potentially work better here for certain images. I went with the cosine because it gave a good response for the base case I tested.
```
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace FakeMini
{
    static class Program
    {
        static void Main()
        {
            // Some tuning variables
            double saturationValue = 1.7;
            double contrastValue = 1.2;
            int gaussianSize = 13; // Must be odd and >1 (3, 5, 7...)
            // NxN Gaussian kernel
            int padding = gaussianSize / 2;
            double[,] kernel = GenerateGaussianKernel(gaussianSize);
            Bitmap src = null;
            using (var img = new Bitmap(File.OpenRead("in.jpg")))
            {
                src = new Bitmap(img);
            }
            // Bordering could be avoided by reflecting or wrapping instead
            // Also takes advantage of the fact that a new bitmap returns zeros from GetPixel
            Bitmap border = new Bitmap(src.Width + padding*2, src.Height + padding*2);
            // Get average intensity of entire image
            double intensity = 0;
            for (int x = 0; x < src.Width; x++)
            {
                for (int y = 0; y < src.Height; y++)
                {
                    intensity += src.GetPixel(x, y).GetBrightness();
                }
            }
            double averageIntensity = intensity / (src.Width * src.Height);
            // Modify saturation and contrast
            double brightness;
            double saturation;
            for (int x = 0; x < src.Width; x++)
            {
                for (int y = 0; y < src.Height; y++)
                {
                    Color oldPx = src.GetPixel(x, y);
                    brightness = oldPx.GetBrightness();
                    saturation = oldPx.GetSaturation() * saturationValue;
                    Color newPx = FromHSL(
                                oldPx.GetHue(),
                                Clamp(saturation, 0.0, 1.0),
                                Clamp(averageIntensity - (averageIntensity - brightness) * contrastValue, 0.0, 1.0));
                    src.SetPixel(x, y, newPx);
                    border.SetPixel(x+padding, y+padding, newPx);
                }
            }
            // Apply gaussian blur, weighted by corresponding sine value based on height
            double blurWeight;
            Color oldColor;
            Color newColor;
            for (int x = padding; x < src.Width+padding; x++)
            {
                for (int y = padding; y < src.Height+padding; y++)
                {
                    oldColor = border.GetPixel(x, y);
                    newColor = Convolve2D(
                        kernel,
                        GetNeighbours(border, gaussianSize, x, y)
                       );
                    // sqrt([cos(pi*x_norm)^2 + cos(pi*y_norm)^2]/2) gives a decent response
                    blurWeight = Clamp(Math.Sqrt(
                        Math.Pow(Math.Cos(Math.PI * (y - padding) / src.Height), 2) +
                        Math.Pow(Math.Cos(Math.PI * (x - padding) / src.Width), 2)/2.0), 0.0, 1.0);
                    src.SetPixel(
                        x - padding,
                        y - padding,
                        Color.FromArgb(
                            Convert.ToInt32(Math.Round(oldColor.R * (1 - blurWeight) + newColor.R * blurWeight)),
                            Convert.ToInt32(Math.Round(oldColor.G * (1 - blurWeight) + newColor.G * blurWeight)),
                            Convert.ToInt32(Math.Round(oldColor.B * (1 - blurWeight) + newColor.B * blurWeight))
                            )
                        );
                }
            }
            border.Dispose();
            // Configure some save parameters
            EncoderParameters ep = new EncoderParameters(3);
            ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
            ep.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.ScanMethod, (int)EncoderValue.ScanMethodInterlaced);
            ep.Param[2] = new EncoderParameter(System.Drawing.Imaging.Encoder.RenderMethod, (int)EncoderValue.RenderProgressive);
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
            ImageCodecInfo ici = null;
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.MimeType == "image/jpeg")
                    ici = codec;
            }
            src.Save("out.jpg", ici, ep);
            src.Dispose();
        }
        // Create RGB from HSL
        // (C# BCL allows me to go one way but not the other...)
        private static Color FromHSL(double h, double s, double l)
        {
            int h0 = Convert.ToInt32(Math.Floor(h / 60.0));
            double c = (1.0 - Math.Abs(2.0 * l - 1.0)) * s;
            double x = (1.0 - Math.Abs((h / 60.0) % 2.0 - 1.0)) * c;
            double m = l - c / 2.0;
            int m0 = Convert.ToInt32(255 * m);
            int c0 = Convert.ToInt32(255*(c + m));
            int x0 = Convert.ToInt32(255*(x + m));
            switch (h0)
            {
                case 0:
                    return Color.FromArgb(255, c0, x0, m0);
                case 1:
                    return Color.FromArgb(255, x0, c0, m0);
                case 2:
                    return Color.FromArgb(255, m0, c0, x0);
                case 3:
                    return Color.FromArgb(255, m0, x0, c0);
                case 4:
                    return Color.FromArgb(255, x0, m0, c0);
                case 5:
                    return Color.FromArgb(255, c0, m0, x0);
            }
            return Color.FromArgb(255, m0, m0, m0);
        }
        // Just so I don't have to write "bool ? val : val" everywhere
        private static double Clamp(double val, double min, double max)
        {
            if (val >= max)
                return max;
            else if (val <= min)
                return min;
            else
                return val;
        }
        // Simple convolution as C# BCL doesn't appear to have any
        private static Color Convolve2D(double[,] k, Color[,] n)
        {
            double r = 0;
            double g = 0;
            double b = 0;
            for (int i=0; i<k.GetLength(0); i++)
            {
                for (int j=0; j<k.GetLength(1); j++)
                {
                    r += n[i,j].R * k[i,j];
                    g += n[i,j].G * k[i,j];
                    b += n[i,j].B * k[i,j];
                }
            }
            return Color.FromArgb(
                Convert.ToInt32(Math.Round(r)),
                Convert.ToInt32(Math.Round(g)),
                Convert.ToInt32(Math.Round(b)));
        }
        // Generates a simple 2D square (normalized) Gaussian kernel based on a size
        // No tuning parameters - just using sigma = 1 for each
        private static double [,] GenerateGaussianKernel(int n)
        {
            double[,] kernel = new double[n, n];
            double currentValue;
            double normTotal = 0;
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    currentValue = Math.Exp(-(Math.Pow(i - n / 2, 2) + Math.Pow(j - n / 2, 2)) / 2.0);
                    kernel[i, j] = currentValue;
                    normTotal += currentValue;
                }
            }
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    kernel[i, j] /= normTotal;
                }
            }
            return kernel;
        }
        // Gets the neighbours around the current pixel
        private static Color[,] GetNeighbours(Bitmap bmp, int n, int x, int y)
        {
            Color[,] neighbours = new Color[n, n];
            for (int i = -n/2; i < n-n/2; i++)
            {
                for (int j = -n/2; j < n-n/2; j++)
                {
                    neighbours[i+n/2, j+n/2] = bmp.GetPixel(x + i, y + j);
                }
            }
            return neighbours;
        }
    }
}
```
[Answer]
# Java
Uses a fast running-average two-way box blur to be speedy enough to run multiple passes, emulating a Gaussian blur. Blur is an elliptical gradient instead of bi-linear as well.
Visually, it works best on large images. Has a darker, grungier theme.
I'm happy with how the blur turned out on appropriately-sized images, it's quite gradual and hard to discern where it "begins."
All computations done on arrays of integers or doubles (for HSV).
Expects file path as argument, outputs file to same location with suffix " miniaturized.png"
Also displays the input and output in a JFrame for immediate viewing.
(click to see large versions, they're way better)
### Before:
[![https://i.stack.imgur.com/l16lH.jpg](https://i.stack.imgur.com/ztqCu.jpg)](https://i.stack.imgur.com/ztqCu.jpg)
### After:
[![enter image description here](https://i.stack.imgur.com/jqHgC.jpg)](https://i.stack.imgur.com/CVyXg.jpg)
I may have to add some smarter tone mapping or luma preservation, as it can get quite dark:
### Before:
[![enter image description here](https://i.stack.imgur.com/HPcTM.jpg)](https://i.stack.imgur.com/wm1Ba.jpg)
### After:
[![enter image description here](https://i.stack.imgur.com/nEn2G.jpg)](https://i.stack.imgur.com/u5ql8.jpg)
Still interesting though, puts it in a whole new atmosphere.
### The Code:
```
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
public class SceneMinifier {
    
    static final double CONTRAST_INCREASE = 8;
    static final double SATURATION_INCREASE = 7;
    
    public static void main(String[] args) throws IOException {
        
        if (args.length < 1) {
            System.out.println("Please specify an input image file.");
            return;
        }
        
        BufferedImage temp = ImageIO.read(new File(args[0]));
        
        BufferedImage input = new BufferedImage(temp.getWidth(), temp.getHeight(), BufferedImage.TYPE_INT_ARGB);
        input.getGraphics().drawImage(temp, 0, 0, null); // just want to guarantee TYPE_ARGB
        
        int[] pixels = ((DataBufferInt) input.getData().getDataBuffer()).getData();
        
        // saturation
        
        double[][] hsv = toHSV(pixels);
        for (int i = 0; i < hsv[1].length; i++)
            hsv[1][i] = Math.min(1, hsv[1][i] * (1 + SATURATION_INCREASE / 10));
        
        // contrast
        
        int[][] rgb = toRGB(hsv[0], hsv[1], hsv[2]);
        
        double c = (100 + CONTRAST_INCREASE) / 100;
        c *= c;
        
        for (int i = 0; i < pixels.length; i++)
            for (int q = 0; q < 3; q++)
                rgb[q][i] = (int) Math.max(0, Math.min(255, ((rgb[q][i] / 255. - .5) * c + .5) * 255));
        
        // blur
        
        int w = input.getWidth();
        int h = input.getHeight();
        
        int k = 5;
        int kd = 2 * k + 1;
        double dd = 1 / Math.hypot(w / 2, h / 2);
        
        for (int reps = 0; reps < 5; reps++) {
            
            int tmp[][] = new int[3][pixels.length];
            int vmin[] = new int[Math.max(w, h)];
            int vmax[] = new int[Math.max(w, h)];
            
            for (int y = 0, yw = 0, yi = 0; y < h; y++) {
                int[] sum = new int[3];
                for (int i = -k; i <= k; i++) {
                    int ii = yi + Math.min(w - 1, Math.max(i, 0));
                    for (int q = 0; q < 3; q++)
                        sum[q] += rgb[q][ii];
                }
                for (int x = 0; x < w; x++) {
                    
                    int dx = x - w / 2;
                    int dy = y - h / 2;
                    double dist = Math.sqrt(dx * dx + dy * dy) * dd;
                    dist *= dist;
                    
                    for (int q = 0; q < 3; q++)
                        tmp[q][yi] = (int) Math.min(255, sum[q] / kd * dist + rgb[q][yi] * (1 - dist));
                    
                    if (y == 0) {
                        vmin[x] = Math.min(x + k + 1, w - 1);
                        vmax[x] = Math.max(x - k, 0);
                    }
                    
                    int p1 = yw + vmin[x];
                    int p2 = yw + vmax[x];
                    
                    for (int q = 0; q < 3; q++)
                        sum[q] += rgb[q][p1] - rgb[q][p2];
                    yi++;
                }
                yw += w;
            }
            
            for (int x = 0, yi = 0; x < w; x++) {
                int[] sum = new int[3];
                int yp = -k * w;
                for (int i = -k; i <= k; i++) {
                    yi = Math.max(0, yp) + x;
                    for (int q = 0; q < 3; q++)
                        sum[q] += tmp[q][yi];
                    yp += w;
                }
                yi = x;
                for (int y = 0; y < h; y++) {
                    
                    int dx = x - w / 2;
                    int dy = y - h / 2;
                    double dist = Math.sqrt(dx * dx + dy * dy) * dd;
                    dist *= dist;
                    
                    for (int q = 0; q < 3; q++)
                        rgb[q][yi] = (int) Math.min(255, sum[q] / kd * dist + tmp[q][yi] * (1 - dist));
                    
                    if (x == 0) {
                        vmin[y] = Math.min(y + k + 1, h - 1) * w;
                        vmax[y] = Math.max(y - k, 0) * w;
                    }
                    int p1 = x + vmin[y];
                    int p2 = x + vmax[y];
                    
                    for (int q = 0; q < 3; q++)
                        sum[q] += tmp[q][p1] - tmp[q][p2];
                    
                    yi += w;
                }
            }
        }
        
        // pseudo-lighting pass
        
        for (int i = 0; i < pixels.length; i++) {
            int dx = i % w - w / 2;
            int dy = i / w - h / 2;
            double dist = Math.sqrt(dx * dx + dy * dy) * dd;
            dist *= dist;
            
            for (int q = 0; q < 3; q++) {
                if (dist > 1 - .375)
                    rgb[q][i] *= 1 + (Math.sqrt((1 - dist + .125) / 2) - (1 - dist) - .125) * .7;
                if (dist < .375 || dist > .375)
                    rgb[q][i] *= 1 + (Math.sqrt((dist + .125) / 2) - dist - .125) * dist > .375 ? 1 : .8;
                rgb[q][i] = Math.min(255, Math.max(0, rgb[q][i]));
            }
        }
        
        // reassemble image
        
        BufferedImage output = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_INT_ARGB);
        
        pixels = ((DataBufferInt) output.getData().getDataBuffer()).getData();
        
        for (int i = 0; i < pixels.length; i++)
            pixels[i] = 255 << 24 | rgb[0][i] << 16 | rgb[1][i] << 8 | rgb[2][i];
        
        output.setRGB(0, 0, output.getWidth(), output.getHeight(), pixels, 0, output.getWidth());
        
        // display results
        
        display(input, output);
        
        // output image
        
        ImageIO.write(output, "PNG", new File(args[0].substring(0, args[0].lastIndexOf('.')) + " miniaturized.png"));
        
    }
    
    private static int[][] toRGB(double[] h, double[] s, double[] v) {
        int[] r = new int[h.length];
        int[] g = new int[h.length];
        int[] b = new int[h.length];
        
        for (int i = 0; i < h.length; i++) {
            double C = v[i] * s[i];
            double H = h[i];
            double X = C * (1 - Math.abs(H % 2 - 1));
            
            double ri = 0, gi = 0, bi = 0;
            
            if (0 <= H && H < 1) {
                ri = C;
                gi = X;
            } else if (1 <= H && H < 2) {
                ri = X;
                gi = C;
            } else if (2 <= H && H < 3) {
                gi = C;
                bi = X;
            } else if (3 <= H && H < 4) {
                gi = X;
                bi = C;
            } else if (4 <= H && H < 5) {
                ri = X;
                bi = C;
            } else if (5 <= H && H < 6) {
                ri = C;
                bi = X;
            }
            
            double m = v[i] - C;
            
            r[i] = (int) ((ri + m) * 255);
            g[i] = (int) ((gi + m) * 255);
            b[i] = (int) ((bi + m) * 255);
        }
        
        return new int[][] { r, g, b };
    }
    
    private static double[][] toHSV(int[] c) {
        double[] h = new double[c.length];
        double[] s = new double[c.length];
        double[] v = new double[c.length];
        
        for (int i = 0; i < c.length; i++) {
            double r = (c[i] & 0xFF0000) >> 16;
            double g = (c[i] & 0xFF00) >> 8;
            double b = c[i] & 0xFF;
            
            r /= 255;
            g /= 255;
            b /= 255;
            
            double M = Math.max(Math.max(r, g), b);
            double m = Math.min(Math.min(r, g), b);
            double C = M - m;
            
            double H = 0;
            
            if (C == 0)
                H = 0;
            else if (M == r)
                H = (g - b) / C % 6;
            else if (M == g)
                H = (b - r) / C + 2;
            else if (M == b)
                H = (r - g) / C + 4;
            
            h[i] = H;
            s[i] = C / M;
            v[i] = M;
        }
        return new double[][] { h, s, v };
    }
    
    private static void display(final BufferedImage original, final BufferedImage output) {
        
        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        int wt = original.getWidth();
        int ht = original.getHeight();
        double ratio = (double) wt / ht;
        if (ratio > 1 && wt > d.width / 2) {
            wt = d.width / 2;
            ht = (int) (wt / ratio);
        }
        if (ratio < 1 && ht > d.getHeight() / 2) {
            ht = d.height / 2;
            wt = (int) (ht * ratio);
        }
        
        final int w = wt, h = ht;
        
        JFrame frame = new JFrame();
        JPanel pan = new JPanel() {
            BufferedImage buffer = new BufferedImage(w * 2, h, BufferedImage.TYPE_INT_RGB);
            Graphics2D gg = buffer.createGraphics();
            
            {
                gg.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
                gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            }
            
            @Override
            public void paint(Graphics g) {
                gg.drawImage(original, 0, 0, w, h, null);
                gg.drawImage(output, w, 0, w, h, null);
                g.drawImage(buffer, 0, 0, null);
            }
        };
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pan.setPreferredSize(new Dimension(w * 2, h));
        frame.setLayout(new BorderLayout());
        frame.add(pan, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }
}
```
[Answer]
# [J](http://jsoftware.com)
This was a nice challenge. The settings for blur, saturation, and contrast are hard-coded but can be easily changed if desired. However, the area in focus is hard-coded as a horizontal line in the center. It can not be simply modified like the other settings. It was chosen since most of the test images feature views across a city.
After performing a Gaussian blur, I split the image horizontally into 5 regions. The top and bottom regions will receive 100% of the blur. The middle region will receive 0% of the blur. The two remaining regions will both scale proportionally to the inverse cube from 0% to 100%.
The code is to be used as a script in J and that script will be in the same folder as `input.bmp` which will be the input image. It will create `output.bmp` which will be a *fake miniature* of the input.
The performance is good and on my pc using an i7-4770k, it takes about 20 seconds to process an image from the OP's set. Previously, it took about 70 seconds to process an image using standard convolution with the `;._3` subarray operator. The performance was improved by using FFT to perform convolution.
[![Loop](https://i.stack.imgur.com/xg5r1.png)](https://i.stack.imgur.com/nGGb6.jpg)
[![Loop-Mini](https://i.stack.imgur.com/WDF7V.jpg)](https://i.stack.imgur.com/CwqIV.jpg)
[![City](https://i.stack.imgur.com/JZX4v.png)](https://i.stack.imgur.com/8MwQQ.jpg)
[![City-Mini](https://i.stack.imgur.com/6QNMU.jpg)](https://i.stack.imgur.com/HVdbG.jpg)
This code requires the `bmp` and `math/fftw` addons to be installed. You can install them using `install 'bmp'` and `install 'math/fftw'`. Your system may also need the `fftw` library to be installed.
```
load 'bmp math/fftw'
NB. Define 2d FFT
fft2d =: 4 : 0
  s =. $ y
  i =. zzero_jfftw_ + , y
  o =. 0 * i
  p =. createplan_jfftw_ s;i;o;x;FFTW_ESTIMATE_jfftw_
  fftwexecute_jfftw_ p
  destroyplan_jfftw_ p
  r =. s $ o
  if. x = FFTW_BACKWARD_jfftw_ do.
    r =. r % */ s
  end.
  r
)
fft2 =: (FFTW_FORWARD_jfftw_ & fft2d) :. (FFTW_BACKWARD_jfftw & fft2d)
ifft2 =: (FFTW_BACKWARD_jfftw_ & fft2d) :. (FFTW_FORWARD_jfftw & fft2d)
NB. Settings: Blur radius - Saturation - Contrast
br =: 15
s =: 3
c =: 1.5
NB. Read image and extract rgb channels
i =: 255 %~ 256 | (readbmp 'input.bmp') <.@%"_ 0 ] 2 ^ 16 8 0
'h w' =: }. $ i
NB. Pad each channel to fit Gaussian blur kernel
'hp wp' =: (+: br) + }. $ i
ip =: (hp {. wp {."1 ])"_1 i
NB. Gaussian matrix verb
gm =: 3 : '(%+/@,)s%~2p1%~^(s=.*:-:y)%~-:-+&*:/~i:y'
NB. Create a Gaussian blur kernel
gk =: fft2 hp {. wp {."1 gm br
NB. Blur each channel using FFT-based convolution and unpad
ib =: (9 o. (-br) }. (-br) }."1 br }. br }."1 [: ifft2 gk * fft2)"_1 ip
NB. Create the blur gradient to emulate tilt-shift
m =: |: (w , h) $ h ({. >. (({.~ -)~ |.)) , 1 ,: |. (%~i.) 0.2 I.~ (%~i.) h
NB. Tilt-shift each channel
it =: i ((m * ]) + (-. m) * [)"_1 ib
NB. Create the saturation matrix
sm =: |: ((+ ] * [: =@i. 3:)~ 3 3 |:@$ 0.299 0.587 0.114 * -.) s
NB. Saturate and clamp
its =: 0 >. 1 <. sm +/ .*"1 _ it
NB. Contrast and clamp
itsc =: 0 >. 1 <. 0.5 + c * its - 0.5
NB. Output the image
'output.bmp' writebmp~ (2 <.@^ 16 8 0) +/ .* 255 <.@* itsc
exit ''
```
]