/* This file was generated by scramble.py. */
#include "hough.h"
#include "scramble.h"
static int ** lookup_table(int min_r, int max_r)
{
    int nr = 1 + max_r - min_r;
    int **lut = ALLOCATE(nr * sizeof *lut);
    for (int r = min_r; r <= max_r; r++)
    {
        int ir = r - min_r;
        /* we use pixel resolution for the circumference of the circle */
        int na = 2 * pi * r;
        lut[ir] = ALLOCATE(1 + na * 2 * sizeof *lut[ir]);
        lut[ir][0] = na;
        double a = 0, da = 2 * pi / na;
        for (int ia = 0; ia < na; ia++)
        {
            int rcos = round(r * cos(a));
            int rsin = round(r * sin(a));
            lut[ir][1 + ia * 2 + 0] = rcos;
            lut[ir][1 + ia * 2 + 1] = rsin;
            a += da;
        }
    }
    return lut;
}
Vector * hough_circle(Image *image, int min_r, int max_r, int min_cx, int max_cx, int min_cy, int max_cy, int threshold, int hint_r, int hint_x, int hint_y, char const *what, char const *outputdir, char const *outputwhat)
{
    /* 
     * We use a three dimensional parameter space a, b, r. Pixels on a circle are
     * given by:
     * x = a + r * cos(theta)
     * y = b + r * sin(theta)
     * theta [0..360)
     * That is, a and b are the center ofthe circle, and r is the radius.
     * 
     * Now, if we find that a point x/y in our image lies on a circle, then we
     * accumulate each possible a/b/r position in hough space.
     * 
     * For example, we search for a circle with radius 5. We have a circle pixel at
     * 0/0. Which possible a/b combinations would this contribute to? In Hough
     * space it are all a/b where a circle with center a/b would include the point.
     * For example, a circle at 5/0 would include 0/0, as would a circle at 0/5. In
     * short, we draw a circle around 0/0 with radius 5 in Hough space.
     * 
     * The min/max parameters are ranges for the radius and the center position
     * relative to the image. The radius as well as the center range may also
     * extend to outside the image area. */
    int output_accums = 0;
    if (what && strstr(what, outputwhat))
    {
        output_accums = 1;
    }
    int w = image->w;
    int h = image->h;
    B *data = image->data.b;
    Vector *circles = vector_new(sizeof(HoughCircle));
    int **lut = lookup_table(min_r, max_r);
    int nx = 1 + max_cx - min_cx;
    int ny = 1 + max_cy - min_cy;
    int *hough = ALLOCATE(nx * ny * sizeof *hough);
    Image *out = None;
    if (output_accums)
    {
        out = image_new_undef_b(nx, ny);
    }
    for (int r = min_r; r <= max_r; r++)
    {
        CLEAR(hough, nx * ny * sizeof *hough);
        int ir = r - min_r;
        int *rlut = lut[ir];
        /* ok, draw some circles */
        for (int j = 0; j < h; j++)
        {
            for (int i = 0; i < w; i++)
            {
                B c = data[j * w + i];
                if (c)
                {
                    int na = rlut[0];
                    for (int ia = 0; ia < na; ia++)
                    {
                        int rcos = rlut[1 + 2 * ia + 0];
                        int rsin = rlut[1 + 2 * ia + 1];
                        int a = i - rcos;
                        int b = j - rsin;
                        if (a >= min_cx && a <= max_cx && b >= min_cy && b <= max_cy)
                        {
                            hough[a - min_cx + nx * (b - min_cy)] += c;
                            /* Since we draw non-antialiased and rather crude circles, pixels likely */
                            /* can be off by a pixel or so. Therefore we collect neighbor */
                            /* pixels for each pixel as well. */
                        }
                    }
                }
            }
        }
        for (int j = 1; j < ny - 1; j++)
        {
            for (int i = 3; i < nx - 3; i++)
            {
                int sum = 0;
                for (int k = -1; k <= 1; k++)
                {
                    for (int l = -3; l <= 3; l++)
                    {
                        sum += hough[(i + l) + (j + k) * nx];
                    }
                }
                hough[i + j * nx] = sum / 21;
                /* If we have a hint circle, anything outside is discarded. */
            }
        }
        if (hint_r)
        {
            int rr = hint_r * hint_r;
            for (int j = 0; j < ny; j++)
            {
                for (int i = 0; i < nx; i++)
                {
                    int dx = min_cx + i - hint_x;
                    int dy = min_cy + j - hint_y;
                    if (dx * dx + dy * dy > rr)
                    {
                        hough[i + j * nx] = 0;
                        /* find maximum accumulator value */
                    }
                }
            }
        }
        int max = 1;
        for (int j = 0; j < ny; j++)
        {
            for (int i = 0; i < nx; i++)
            {
                if (hough[i + j * nx] > max)
                {
                    max = hough[i + j * nx];
                    /* TODO: As we actually will get slight ellipses most of the time, instead */
                    /* of non-maximum suppression, some kind of averaging would be better. */
                    /* _ _ */
                    /* / X \ */
                    /* | | | | */
                    /* \_X_/ */
                    /* A B */
                    /* _ */
                    /* / \ */
                    /* |   | */
                    /* \_/ */
                    /* C */
                    /*  */
                    /* Instead of choosing either one of found circles A and B at the two */
                    /* ends of the ellipse, we want C averaged by them (maybe with the */
                    /* radius slightly compensated). */
                    /* create a grayscale image, with non-maxima suppressed and faint circles */
                    /* thresholded */
                }
            }
        }
        for (int j = 0; j < ny; j++)
        {
            for (int i = 0; i < nx; i++)
            {
                int v = hough[i + j * nx];
                if (i < nx - 1 && hough[i + 1 + j * nx] > v)
                {
                    v = 0;
                }
                if (j > 0 && hough[i + (j - 1) * nx] > v)
                {
                    v = 0;
                }
                if (i > 0 && hough[i - 1 + j * nx] > v)
                {
                    v = 0;
                }
                if (j < ny - 1 && hough[i + (j + 1) * nx] > v)
                {
                    v = 0;
                }
                int cv = v * 255 / max;
                if (cv < threshold)
                {
                    v = 0;
                    /* cv = 0 */
                }
                if (out)
                {
                    out->data.b[i + j * nx] = cv;
                }
                if (v)
                {
                    HoughCircle c = {min_cx + i, min_cy + j, r, 2 * pi * r, v, 2 * pi * r * 255};
                    vector_add(circles, &c);
                }
            }
        }
        if (output_accums)
        {
            char name[MAX_PATH];
            sprintf(name, "%s/%s_radius_%d.tiff", outputdir, image->name, r);
            save_tiff_luminance(name, out);
        }
    }
    if (out)
    {
        image_del(out);
    }
    return circles;
}
int find_best_circle(Vector *circles, int *x, int *y, int *r, int hint, int hint_x, int hint_y, int hint_r, int hint_weight, int debug)
/* FIXME: need to find a better heuristic to get our score.. */
{
    int FAIL = -1000;
    int best_accum = FAIL;
    HoughCircle *best = None;
    if (! circles->count)
    {
        return 0;
    }
    int mindist = 0;
    int maxdist = 0;
    if (hint)
    /* Find minimum and maximum distance from hint */
    {
        for (int i = 0; i < circles->count; i++)
        {
            HoughCircle *c = vector_get(circles, i);
            int dx = c->x - hint_x;
            int dy = c->y - hint_y;
            int dist =sqrt(dx * dx + dy * dy);
            if (mindist == 0 || dist < mindist)
            {
                mindist = dist;
            }
            if (dist > maxdist)
            {
                maxdist = dist;
            }
        }
    }
    int middist = (mindist + maxdist) / 2;
    int medium_coverage = 0;
    for (int i = 0; i < circles->count; i++)
    {
        HoughCircle *c = vector_get(circles, i);
        int coverage = c->accum * 1000 / c->max;
        medium_coverage += coverage;
    }
    medium_coverage /= circles->count;
    /* Square of radius were distance from hint-circle is unscaled. */
    int qdist = 5 * 5;
    if (debug)
    {
        printf("Selecting best circle..\n");
    }
    for (int i = 0; i < circles->count; i++)
    {
        HoughCircle *c = vector_get(circles, i);
        int score;
        int dist = 0;
        int coverage = c->accum * 1000 / c->max;
        if (hint)
        /* Prefer circles with center closer to the hint position */
        {
            int dx = c->x - hint_x;
            int dy = c->y - hint_y;
            dist = sqrt(dx * dx + dy * dy);
            if (dist > middist)
            {
                score /= 2;
            }
            if (dist > hint_r)
            {
                if (debug)
                {
                    printf(" %d @ %d/%d: %d > %d therefore out\n", c->radius, c->x, c->y, dist, hint_r);
                }
                continue;
                /* This gives bigger circles an advantages, as they have more pixels in */
                /* total. */
            }
        }
        score = c->accum;
        /* this means for bigger circles, distance weighs more */
        score -= dist * dist * hint_weight * c->radius / qdist;
        /* this usually has no effect, but for completely identical scores */
        /* otherwise, we pefer the "outer" outline */
        score += c->radius;
        /* FIXME: this works usually better */
        if (0)
        {
            if (coverage < medium_coverage)
            {
                score /= 2;
            }
        }
        else
        {
            score *= coverage;
        }
        if (debug)
        {
            printf(" %d @ %d/%d: %d (%d/%d dis) [%d/%d cov (%d/%d)]\n", c->radius, c->x, c->y, score, dist, middist, coverage, medium_coverage, c->accum, c->max);
        }
        if (score > best_accum)
        {
            best_accum = score;
            best = c;
        }
    }
    if (debug)
    {
        printf("and the winner is: %d for %d @ %d/%d\n", best_accum, best->radius,  best->x, best->y);
    }
    if (best_accum == FAIL)
    {
        return 0;
    }
    *x = best->x;
    *y = best->y;
    *r = best->radius;
    return 1;
}
/* This file was generated by scramble.py. */
