source: libpipi/trunk/pipi/dither/dbs.c @ 2666

Revision 2666, 7.3 KB checked in by sam, 5 years ago (diff)
  • Prefix dithering functions with _dither_ to avoid namespace cluttering.
Line 
1/*
2 *  libpipi       Proper image processing implementation library
3 *  Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id$
7 *
8 *  This library is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 * dbs.c: Direct Binary Search dithering functions
17 */
18
19#include "config.h"
20#include "common.h"
21
22#include <string.h>
23#include <stdlib.h>
24
25#include <math.h>
26
27#include "pipi.h"
28#include "pipi_internals.h"
29
30#define CELL 16
31
32#define N 7
33#define NN ((N * 2 + 1))
34
35/* FIXME: though the algorithm is supposed to stop, we do not have a real,
36 * guaranteed stop condition here. */
37
38pipi_image_t *pipi_dither_dbs(pipi_image_t *src)
39{
40    double kernel[NN * NN];
41    double t = 0.;
42    pipi_image_t *dst, *tmp1, *tmp2;
43    pipi_pixels_t *srcp, *dstp, *tmp1p, *tmp2p;
44    int *changelist;
45    float *srcdata, *dstdata, *tmp1data, *tmp2data;
46    int i, j, x, y, w, h, cw, ch;
47
48    /* Build our human visual system kernel. */
49    for(j = 0; j < NN; j++)
50        for(i = 0; i < NN; i++)
51        {
52            double a = (double)(i - N);
53            double b = (double)(j - N);
54            kernel[j * NN + i] =
55                    exp(-(a * a + b * b) / (2. * 1.6 * 1.6))
56                  + exp(-(a * a + b * b) / (2. * 0.6 * 0.6));
57            t += kernel[j * NN + i];
58        }
59
60    for(j = 0; j < NN; j++)
61        for(i = 0; i < NN; i++)
62            kernel[j * NN + i] /= t;
63
64    w = src->w;
65    h = src->h;
66
67    cw = (w + CELL - 1) / CELL;
68    ch = (h + CELL - 1) / CELL;
69    changelist = malloc(cw * ch * sizeof(int));
70    memset(changelist, 0, cw * ch * sizeof(int));
71
72    srcp = pipi_getpixels(src, PIPI_PIXELS_Y_F);
73    srcdata = (float *)srcp->pixels;
74
75    tmp1 = pipi_convolution(src, NN, NN, kernel);
76    tmp1p = pipi_getpixels(tmp1, PIPI_PIXELS_Y_F);
77    tmp1data = (float *)tmp1p->pixels;
78
79    /* The initial dither is an empty image. So is its blurred version,
80     * but I leave the pipi_convolution() call here in case we choose
81     * to change the way to create the initial dither. */
82    dst = pipi_new(w, h);
83    dstp = pipi_getpixels(dst, PIPI_PIXELS_Y_F);
84    dstdata = (float *)dstp->pixels;
85
86    for(y = 0; y < h; y++)
87        for(x = 0; x < w; x++)
88            dstdata[y * w + x] = srcdata[y * w + x] > 0.5 ? 1.0 : 0.0;
89
90    tmp2 = pipi_convolution(dst, NN, NN, kernel);
91    tmp2p = pipi_getpixels(tmp2, PIPI_PIXELS_Y_F);
92    tmp2data = (float *)tmp2p->pixels;
93
94    for(;;)
95    {
96        int cx, cy, n;
97        int allchanges = 0;
98
99        for(cy = 0; cy < ch; cy++)
100        for(cx = 0; cx < cw; cx++)
101        {
102            int changes = 0;
103
104            if(changelist[cy * cw + cx] >= 2)
105                continue;
106
107            for(y = cy * CELL; y < (cy + 1) * CELL; y++)
108            for(x = cx * CELL; x < (cx + 1) * CELL; x++)
109            {
110                double d, d2, e, best = 0.;
111                int opx = -1, opy = -1;
112
113                d = dstdata[y * w + x];
114
115                /* Compute the effect of a toggle */
116                e = 0.;
117                for(j = -N; j < N + 1; j++)
118                {
119                    if(y + j < 0 || y + j >= h)
120                        continue;
121
122                    for(i = -N; i < N + 1; i++)
123                    {
124                        double m, p, q1, q2;
125
126                        if(x + i < 0 || x + i >= w)
127                            continue;
128
129                        m = kernel[(j + N) * NN + i + N];
130                        p = tmp1data[(y + j) * w + x + i];
131                        q1 = tmp2data[(y + j) * w + x + i];
132                        q2 = q1 - m * d + m * (1. - d);
133                        e += (q1 - p) * (q1 - p) - (q2 - p) * (q2 - p);
134                    }
135                }
136                if(e > best)
137                {
138                    best = e;
139                    opx = opy = 0;
140                }
141
142                /* Compute the effect of swaps */
143                for(n = 0; n < 8; n++)
144                {
145                    static int const step[] =
146                      { 0, 1, 0, -1, -1, 0, 1, 0, -1, -1, -1, 1, 1, -1, 1, 1 };
147                    int idx = step[n * 2], idy = step[n * 2 + 1];
148                    if(y + idy < 0 || y + idy >= h
149                         || x + idx < 0 || x + idx >= w)
150                        continue;
151                    d2 = dstdata[(y + idy) * w + x + idx];
152                    if(d2 == d)
153                        continue;
154                    e = 0.;
155                    for(j = -N; j < N + 1; j++)
156                    {
157                        if(y + j < 0 || y + j >= h)
158                            continue;
159                        if(j - idy + N < 0 || j - idy + N >= NN)
160                            continue;
161                        for(i = -N; i < N + 1; i++)
162                        {
163                            double ma, mb, p, q1, q2;
164                            if(x + i < 0 || x + i >= w)
165                                continue;
166                            if(i - idx + N < 0 || i - idx + N >= NN)
167                                continue;
168                            ma = kernel[(j + N) * NN + i + N];
169                            mb = kernel[(j - idy + N) * NN + i - idx + N];
170                            p = tmp1data[(y + j) * w + x + i];
171                            q1 = tmp2data[(y + j) * w + x + i];
172                            q2 = q1 - ma * d + ma * d2 - mb * d2 + mb * d;
173                            e += (q1 - p) * (q1 - p) - (q2 - p) * (q2 - p);
174                        }
175                    }
176                    if(e > best)
177                    {
178                        best = e;
179                        opx = idx;
180                        opy = idy;
181                    }
182                }
183
184                /* Apply the change if interesting */
185                if(best <= 0.)
186                    continue;
187                if(opx || opy)
188                {
189                    d2 = dstdata[(y + opy) * w + x + opx];
190                    dstdata[(y + opy) * w + x + opx] = d;
191                }
192                else
193                    d2 = 1. - d;
194                dstdata[y * w + x] = d2;
195                for(j = -N; j < N + 1; j++)
196                for(i = -N; i < N + 1; i++)
197                {
198                    double m = kernel[(j + N) * NN + i + N];
199                    if(y + j >= 0 && y + j < h
200                        && x + i >= 0 && x + i < w)
201                    {
202                        t = tmp2data[(y + j) * w + x + i];
203                        tmp2data[(y + j) * w + x + i] = t + m * (d2 - d);
204                    }
205                    if((opx || opy) && y + opy + j >= 0 && y + opy + j < h
206                                    && x + opx + i >= 0 && x + opx + i < w)
207                    {
208                        t = tmp2data[(y + opy + j) * w + x + opx + i];
209                        tmp2data[(y + opy + j) * w + x + opx + i]
210                                = t + m * (d - d2);
211                    }
212                }
213
214                changes++;
215            }
216
217            if(changes == 0)
218                changelist[cy * cw + cx]++;
219
220            allchanges += changes;
221        }
222
223        if(allchanges == 0)
224            break;
225    }
226
227    free(changelist);
228
229    pipi_free(tmp1);
230    pipi_free(tmp2);
231
232    return dst;
233}
234
Note: See TracBrowser for help on using the repository browser.