/*
 *  edd           error diffusion displacement
 *  Copyright (c) 2008 Sam Hocevar <sam@zoy.org>
 *                All Rights Reserved
 *
 *  $Id$
 *
 *  This program is free software. It comes without any warranty, to
 *  the extent permitted by applicable law. You can redistribute it
 *  and/or modify it under the terms of the Do What The Fuck You Want
 *  To Public License, Version 2, as published by Sam Hocevar. See
 *  http://sam.zoy.org/wtfpl/COPYING for more details.
 */

/* This program computes the Floyd-Steinberg error diffusion algorithm's
 * displacement on the given input image. Error diffusion displacement is
 * introduced in the paper "Reinstating Floyd-Steinberg: Improved Metrics
 * for Quality Assessment of Error Diffusion Algorithms.", ICISP 2008
 * Proceedings. Lecture Notes in Computer Science 5099 Springer 2008, ISBN
 * 978-3-540-69904-0.
 *
 * The resulting dx/dy values are usually around 0.16/0.26 for images that
 * are not entirely black and white. */

#include "config.h"
#include "common.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <pipi.h>

#define Z 3

int main(int argc, char *argv[])
{
    double sigma = 1.2, precision = 0.001, step = 2.;
    double best = 1., fx = -1., fy = -1., bfx = 0., bfy = 0.;
    double e, e0, e1;
    pipi_image_t *img, *kernel, *gauss, *dither, *tmp;
    int dx, dy;

    if(argc < 2)
    {
        fprintf(stderr, "%s: too few arguments\n", argv[0]);
        fprintf(stderr, "Usage: %s <image>\n", argv[0]);
        return EXIT_FAILURE;
    }

    /* Load image, convert it to grayscale, dither it with Floyd-Steinberg */
    img = pipi_load(argv[1]);
    pipi_getpixels(img, PIPI_PIXELS_Y_F);
    gauss = pipi_gaussian_blur(img, sigma);
    kernel = pipi_load("ediff:fs");
    dither = pipi_dither_ediff(img, kernel, PIPI_SCAN_RASTER);
    pipi_free(kernel);
    pipi_free(img);

    /* Compute the standard error */
    tmp = pipi_gaussian_blur(dither, sigma);
    e0 = pipi_measure_msd(gauss, tmp);
    pipi_free(tmp);

    /* Compute the fast error */
    tmp = pipi_gaussian_blur_ext(dither, sigma, sigma, 0.0, 0.16, 0.26);
    e1 = pipi_measure_msd(gauss, tmp);
    pipi_free(tmp);

    /* Compute displacement */
    while(step > precision)
    {
        for(dy = 0; dy <= Z; dy++)
            for(dx = 0; dx <= Z; dx++)
            {
                tmp = pipi_gaussian_blur_ext(dither, sigma, sigma, 0.0,
                                             fx + step * dx / Z,
                                             fy + step * dy / Z);
                e = pipi_measure_msd(gauss, tmp);
                pipi_free(tmp);
                if(e < best)
                {
                    best = e;
                    bfx = fx + step * dx / Z;
                    bfy = fy + step * dy / Z;
                }
            }

        fx = bfx - step / Z;
        fy = bfy - step / Z;
        step = step * 2 / Z;
    }

    printf("E: %g E_fast: %g E_min: %g dx: %g dy: %g\n", e0, e1, best, fx, fy);

    pipi_free(dither);
    pipi_free(gauss);

    return 0;
}